Pipeline Migration
MicrosoftOfficialPublished by Microsoft
- Skill
- Workflow
- Cloud and DevOps
- MIT
- No issues found
AutoGineer summary
Written by AutoGineer from the skill’s own files, not by the author
This skill provides a structured, API-driven workflow for migrating Synapse Data Factory pipelines to Microsoft Fabric. It handles the transformation of pipeline JSON, including inlining datasets, converting linked services to Fabric connections, mapping SynapseNotebook activities to TridentNotebook, and migrating global parameters to Variable Libraries. The skill includes assessment, orchestration, and validation phases to ensure a smooth transition, while identifying unsupported activities that require manual intervention.
What it does
- Assesses Synapse pipeline complexity and migration scope.
- Automates the conversion of Synapse pipeline JSON to Fabric DataPipeline format.
- Inlines dataset definitions directly into pipeline activities.
- Maps Synapse linked services to Fabric connections.
- Converts global parameters into Fabric Variable Library items.
- Updates notebook activities to reference Fabric notebook GUIDs.
When to use it
- When migrating existing Synapse Data Factory pipelines to Microsoft Fabric.
- To assess the feasibility and blockers of a pipeline migration project.
- When you need to convert Synapse global parameters to Fabric Variable Libraries.
- When you need to update notebook activity references for the Fabric environment.
Advantages
- Reduces manual effort by automating JSON transformations and API calls.
- Provides a clear, phased approach to migration with built-in assessment.
- Ensures compatibility by mapping deprecated Synapse features to Fabric equivalents.
- Reduces errors by automating GUID lookups and connection references.
Usage examples
- “Assess my Synapse workspace 'FinancePipelineWS' for migration to Fabric.”
- The agent will query the Synapse workspace, analyze the pipelines, and generate a markdown report detailing the migration scope, potential blockers, and unsupported activities.
- “Migrate all pipelines from Synapse workspace 'FinancePipelineWS' to Fabric workspace 'FinanceFabricWS'.”
- The agent will orchestrate the migration, including creating connections, converting parameters, updating notebook references, and deploying the new pipelines to Fabric.
Install
Download the skill as a ZIP:
https://autogineer.com/agent-skills/microsoft/pipeline-migration/downloadUnzip it into the cross-client skills folder of your project:
mkdir -p .agents/skills && unzip pipeline-migration.zip -d .agents/skills
Documentation by Microsoft
Telemetry — MANDATORY. Every
api.fabric.microsoft.comcall must carryx-ms-fabric-skill: pipeline-migration(az rest:--headers "x-ms-fabric-skill=pipeline-migration"), including every LRO poll,fabric_lroand retry. Snippets omit it — add it anyway.
CRITICAL NOTES
To find workspace details (including its ID) from a workspace name: list all workspaces, then use JMESPath filtering
To find item details (including its ID) from workspace ID, item type, and item name: list all items of that type in that workspace, then use JMESPath filtering
Fabric does NOT have a "Dataset" item type — all dataset properties are inlined into activity
typePropertiesLinked Services map to Fabric Connections — in pipeline activity JSON,
referenceNameon the activity'slinkedServiceblock uses the Fabric connection display name (not the connection GUID); the connection GUID is used only on Fabric REST API callsNotebook activities change from
SynapseNotebooktoTridentNotebookand reference notebooks by GUID, not by nameSynapse global parameters become a Variable Library item in Fabric, referenced as
@pipeline().libraryVariables.<name>. Variable LibraryNumbertypes are not consumable in pipelines — SynapseFloat/Doubleare mapped toStringfor runtime compatibilityThe
Validationactivity type does not exist in Fabric — it must be rewritten asGetMetadata+IfConditionTriggers are intentionally excluded from this skill — recreate schedules manually in Fabric after migration
SSIS package execution, SHIR-exclusive connectors, and Databricks activities are parked — see pipeline-gotchas.md
Synapse Pipelines → Microsoft Fabric Data Factory Migration
Prerequisite Knowledge
These companion documents provide general Fabric REST patterns. Do NOT read them upfront — reference only when a specific phase requires a pattern not already covered in this skill's resource files:
COMMON-CORE.md — General Fabric REST API patterns, authentication & token audiences, item discovery via JMESPath
COMMON-CLI.md —
az rest/az loginCLI patterns, authentication recipes, pipeline run/schedule operationsITEM-DEFINITIONS-CORE.md —
DataPipelineandVariableLibraryitem definition structures (pipeline-content.json, variables.json)SPARK-AUTHORING-CORE.md — Fabric notebook item creation (needed when notebook items don't exist yet in Fabric)
For the notebook side of the migration (the notebooks that pipeline activities call), use the companion synapse-migration skill to migrate the notebook content itself.
Table of Contents
| Topic | Reference |
|---|---|
| Pre-Migration Assessment (run first) | pipeline-assessment.md |
| Migration Orchestrator | pipeline-orchestrator.md |
| API-Driven Migration Workflow | § API-Driven Migration Workflow |
| Activity Type Mapping | activity-mapping.md |
| Notebook Activity Migration (primary focus) | notebook-activity-migration.md |
| Linked Services → Connections | linked-service-to-connection.md |
| Dataset Inlining | dataset-inlining.md |
| Global Parameters → Variable Library | global-parameters-to-variable-library.md |
| Pipeline Gotchas & Parked Activities | pipeline-gotchas.md |
| Validation & Testing | validation-testing.md |
| Migration Report | migration-report.md |
Context Loading Guide
IMPORTANT — Load only what you need. Do NOT read all resource files upfront.
| When | Read This File |
|---|---|
| User asks for an assessment, scope, or plan before migrating | pipeline-assessment.md |
| User asks to migrate a full pipeline workspace | pipeline-orchestrator.md |
| User asks about activity type mapping or unsupported activities | activity-mapping.md |
| User has SynapseNotebook activities (most common) | notebook-activity-migration.md |
| User asks about linked services or connections | linked-service-to-connection.md |
| User asks about Copy, Lookup, or GetMetadata with datasets | dataset-inlining.md |
| User has global parameters to convert | global-parameters-to-variable-library.md |
| User hits SSIS, SHIR, Databricks, or other blockers | pipeline-gotchas.md |
| Post-migration verification | validation-testing.md |
| Generating a migration summary | migration-report.md |
API-Driven Migration Workflow
Authentication
| Target | Token Audience |
|---|---|
| Synapse Data Plane (pipelines, datasets, linked services) | https://dev.azuresynapse.net |
| Synapse ARM (global parameters, workspace properties) | https://management.azure.com |
| Fabric REST API (create pipelines, connections, Variable Libraries) | https://api.fabric.microsoft.com |
Use
az account get-access-token --resource <audience> --query accessToken -o tsvto acquire tokens.
Synapse Data-Plane API Reference
| Operation | Endpoint |
|---|---|
| List all pipelines | GET https://{ws}.dev.azuresynapse.net/pipelines?api-version=2020-12-01 |
| Get pipeline definition | GET https://{ws}.dev.azuresynapse.net/pipelines/{name}?api-version=2020-12-01 |
| List all datasets | GET https://{ws}.dev.azuresynapse.net/datasets?api-version=2020-12-01 |
| Get dataset definition | GET https://{ws}.dev.azuresynapse.net/datasets/{name}?api-version=2020-12-01 |
| List all linked services | GET https://{ws}.dev.azuresynapse.net/linkedservices?api-version=2020-12-01 |
| Get linked service | GET https://{ws}.dev.azuresynapse.net/linkedservices/{name}?api-version=2020-12-01 |
| Get workspace (global parameters) | GET https://management.azure.com/subscriptions/{subId}/resourceGroups/{rg}/providers/Microsoft.Synapse/workspaces/{ws}?api-version=2021-06-01 |
Fabric API Reference
| Operation | Endpoint |
|---|---|
| List connections | GET https://api.fabric.microsoft.com/v1/connections |
| Create connection | POST https://api.fabric.microsoft.com/v1/connections |
| Create pipeline item | POST https://api.fabric.microsoft.com/v1/workspaces/{wsId}/items |
| Update pipeline definition | POST https://api.fabric.microsoft.com/v1/workspaces/{wsId}/items/{id}/updateDefinition |
| Get pipeline definition | POST https://api.fabric.microsoft.com/v1/workspaces/{wsId}/items/{id}/getDefinition |
| Create Variable Library | POST https://api.fabric.microsoft.com/v1/workspaces/{wsId}/items (type: VariableLibrary) |
| List notebooks in workspace | GET https://api.fabric.microsoft.com/v1/workspaces/{wsId}/notebooks |
Assessment Mode (Optional but Recommended)
Before creating any items in Fabric, run the pipeline assessment to understand scope, complexity, and blockers. The assessment is read-only — it queries Synapse APIs only and produces a markdown report.
Copilot workflow — no Python script file needed:
Ask the user: "What is the name of your Synapse workspace?"
Auto-discover subscription ID and resource group via
az account showandaz synapse workspace showRun the assessment code inline from the terminal
Print the full report output directly in the chat
| When to Use | Action |
|---|---|
| User wants to understand migration scope before committing | Ask for workspace name → load pipeline-assessment.md → run inline |
| User asks "what will and won't migrate?" | Run assessment, present the Executive Summary section |
| User asks for a migration plan or scoping document | Run assessment, print report in chat |
| User has already decided to migrate | Skip assessment — go straight to Migration Phases below |
To also save the report to disk, pass
output_path=f"pipeline-assessment-{SYNAPSE_WS}.md"togenerate_assessment_report(). ThePipelineAssessmentobjects it produces feed directly into the migration scripts in pipeline-orchestrator.md.
Migration Mode (Inline — No Script File Required)
Copilot performs the migration directly from the terminal. No Python files to save or run manually.
Ask the user for:
Synapse workspace name (reuse from assessment if already run)
Fabric workspace name
Which pipelines to migrate — specific names, or
*for allOptional name suffix to append to each pipeline in Fabric (e.g.
_migrated) — leave blank to keep the original name
Everything else is auto-discovered:
Subscription ID and resource group via
az account show+az synapse workspace showFabric workspace ID via
GET /v1/workspacesfiltered by display nameNotebook GUIDs in Fabric via
GET /v1/workspaces/{wsId}/notebooks(required forSynapseNotebook → TridentNotebook)Connection names in Fabric via
GET /v1/connections(when datasets reference linked services)
What is fully automated inline:
✅
SynapseNotebook→TridentNotebook— type rename, GUID lookup, removesparkPool/sessionConfiguration, fix timeout to 12h max✅ All compatible activity types — pass through with minor property adjustments
✅ Dataset inlining into activity
typeProperties✅ Global parameter expressions rewritten to
@pipeline().libraryVariables.<name>✅ Pipeline JSON assembly and deployment to Fabric via REST
Copilot checks before starting — will pause and report if:
A notebook referenced by a
SynapseNotebookactivity does not exist in the Fabric workspace yetA Fabric Connection is missing for a linked service referenced by dataset activities
Load pipeline-orchestrator.md for the complete inline runner.
Migration Phases (Execute in Order)
| Phase | Source | Target | Resource |
|---|---|---|---|
| Phase 0 | Synapse notebooks (referenced by pipeline activities) | Fabric Notebooks | synapse-migration skill |
| Phase 1 | Synapse global parameters | Fabric Variable Library | global-parameters-to-variable-library.md |
| Phase 2 | Synapse linked services | Fabric Connections | linked-service-to-connection.md |
| Phase 3 | Synapse datasets | Inlined into activities | dataset-inlining.md |
| Phase 4 | Synapse pipeline activities | Fabric pipeline activities | activity-mapping.md + notebook-activity-migration.md |
| Phase 5 | Assembled pipeline JSON | Fabric DataPipeline item | pipeline-orchestrator.md |
| Final | — | Validation | validation-testing.md |
Phase 0 must precede Phase 4: Fabric Notebook GUIDs are needed before TridentNotebook activities can be written. Phase 2 must precede Phase 3: Connection names must exist before they can be referenced in inlined datasets.
Activity Type Quick Reference
Full mapping table, before/after examples, and parking decisions are in activity-mapping.md.
| Synapse Activity | Fabric Equivalent | Status |
|---|---|---|
SynapseNotebook | TridentNotebook | ✅ Migrated — see notebook-activity-migration.md |
Copy | Copy | ✅ Migrated — datasets inlined |
Lookup | Lookup | ✅ Migrated — datasets inlined |
GetMetadata | GetMetadata | ✅ Migrated — datasets inlined |
Validation | GetMetadata + IfCondition | ✅ Migrated — split into 2 activities |
ForEach | ForEach | ✅ Compatible |
IfCondition | IfCondition | ✅ Compatible |
Switch | Switch | ✅ Compatible |
Until | Until | ✅ Compatible |
Wait | Wait | ✅ Compatible |
Fail | Fail | ✅ Compatible |
SetVariable | SetVariable | ✅ Compatible |
AppendVariable | AppendVariable | ✅ Compatible |
ExecutePipeline | ExecutePipeline | ✅ Compatible — add workspaceId |
WebActivity | WebActivity | ✅ Compatible |
Script | Script | ✅ Compatible — update connection refs |
Delete | Delete | ✅ Migrated — datasets inlined |
Filter | Filter | ✅ Compatible |
SparkJobDefinition (Synapse SJD) | SparkJobDefinition | ✅ Update GUID refs |
HDInsightSpark | TridentNotebook or SparkJobDefinition | ⚠️ Rewrite required |
AzureMLBatchExecution | WebActivity | ⚠️ Rewrite as REST call |
AzureFunctionActivity | WebActivity | ⚠️ Rewrite — use function URL + key |
DatabricksNotebook | ⛔ Parked | See pipeline-gotchas.md |
DatabricksSparkJar | ⛔ Parked | See pipeline-gotchas.md |
DatabricksSparkPython | ⛔ Parked | See pipeline-gotchas.md |
ExecuteSSISPackage | ⛔ Parked | See pipeline-gotchas.md |
AzureBatch | ⛔ Parked | No Fabric equivalent |
Custom | ⛔ Parked | No Fabric equivalent |
Must / Prefer / Avoid
MUST DO
Migrate notebooks before pipelines — Fabric TridentNotebook activities require notebook GUIDs, not names. Use the synapse-migration skill first
Create Fabric Connections before building pipeline JSON — linked service names in Synapse become connection references in Fabric; you need the connection names before inlining datasets
Inline all dataset definitions — Fabric Data Factory has no Dataset item type; all
inputs/outputsdataset properties must be embedded in each activityReplace
@pipeline().globalParameters.<name>with@pipeline().libraryVariables.<name>after creating the Variable LibraryReplace Validation activities with a
GetMetadata+IfConditionpair —Validationdoes not exist as an activity type in FabricRemove
sparkPoolandsessionConfigurationfrom migrated notebook activities — pool selection and session config belong in the Fabric Environment attached to the notebook
PREFER
Variable Library with Value Sets for dev/test/prod environments — use
@pipeline().libraryVariables.<name>with environment-specific Value Sets instead of pipeline-level parameters for environment promotionParameterized
notebookParametersover hardcoded values in TridentNotebook activities — mirrors Synapse parameterized notebook patternTest notebook activities individually before running the full migrated pipeline — notebook GUIDs are the most common source of failure
OneLake Lakehouse sources/sinks for Copy activities where applicable — eliminates the need for external connections for data already in OneLake
AVOID
Do not reference notebooks by name in
TridentNotebookactivities — use the GUID from the Fabric workspace notebook listDo not carry over Synapse triggers — they are not migrated by this skill; recreate schedules in Fabric after validating the pipeline
Do not attempt to migrate SSIS, Databricks, or AzureBatch activities without reading pipeline-gotchas.md first — these require manual intervention
Do not hardcode workspace/item GUIDs in pipeline JSON — use Variable Library entries or pipeline parameters so environments can be promoted without editing pipeline JSON
Do not use
@pipeline().globalParameterssyntax after migration — this expression path does not exist in Fabric; all migrated global parameters must be accessed via@pipeline().libraryVariables
Migration Gotchas — Quick Reference
Full troubleshooting guide is in pipeline-gotchas.md.
| # | Flag ID | Issue | Severity | Resolution Summary |
|---|---|---|---|---|
| PG1 | NOTEBOOK_GUID_NOT_FOUND | Notebook not yet migrated to Fabric when building TridentNotebook activity | High | Run synapse-migration skill first; get GUID from GET /v1/workspaces/{wsId}/notebooks |
| PG2 | DATASET_NOT_INLINED | Activity still references a named dataset (not valid in Fabric) | High | Apply dataset-inlining.md patterns to embed dataset properties into activity source/sink |
| PG3 | GLOBAL_PARAM_EXPRESSION | @pipeline().globalParameters.<name> expression left in migrated pipeline | High | Replace with @pipeline().libraryVariables.<name> after creating Variable Library |
| PG4 | VALIDATION_ACTIVITY_UNSUPPORTED | Validation activity type left in pipeline JSON | High | Rewrite as GetMetadata + IfCondition — see activity-mapping.md |
| PG5 | SHIR_CONNECTOR_PARKED | Activity uses a linked service backed by a Self-Hosted Integration Runtime | Medium | Must set up on-premises data gateway in Fabric; see pipeline-gotchas.md |
| PG6 | SSIS_ACTIVITY_PARKED | ExecuteSSISPackage activity cannot be migrated | High | Parked — no Fabric equivalent; see pipeline-gotchas.md for alternatives |
| PG7 | DATABRICKS_ACTIVITY_PARKED | Databricks activity type has no Fabric native equivalent | High | Parked — use Databricks REST API via WebActivity as workaround; see pipeline-gotchas.md |
| PG8 | SPARKPOOL_REF_ORPHANED | sparkPool / targetBigDataPool reference left in TridentNotebook activity | Medium | Remove sparkPool and sessionConfiguration blocks; pool config belongs in Fabric Environment |
| PG9 | EXECUTE_PIPELINE_NO_WORKSPACE | ExecutePipeline activity missing workspaceId for referenced pipeline | Medium | Add workspaceId to typeProperties; required even for same-workspace child pipelines — omitting it causes runtime failures |
| PG10 | LINKED_SERVICE_NO_CONNECTION | Linked service has no matching Fabric Connection | High | Create connection manually or via API; update connection reference in inlined dataset |
Post-Migration: What's Next
After pipeline migration, hand off to these companion skills and tools:
| Task | Skill / Tool |
|---|---|
| Migrate notebook content (mssparkutils → notebookutils, linked services) | synapse-migration skill |
| Schedule migrated pipelines | COMMON-CLI.md § Job Scheduling |
| Monitor pipeline runs | Fabric workspace → Monitor hub |
| Build new Fabric pipelines | Refer to ITEM-DEFINITIONS-CORE.md § DataPipeline |
| Explore migrated Lakehouse data post-pipeline run | spark-cli or sqldw-cli skill |
Examples
SynapseNotebook → TridentNotebook activity (before/after)
{
"name": "Run_Notebook",
"type": "SynapseNotebook",
"typeProperties": {
"notebook": {"referenceName": "MyNotebook", "type": "NotebookReference"},
"sparkPool": {"referenceName": "BigPool", "type": "BigDataPoolReference"}
}
}After migration (GUID from GET /v1/workspaces/{wsId}/notebooks):
{
"name": "Run_Notebook",
"type": "TridentNotebook",
"typeProperties": {
"notebookId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"workspaceId": "yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy"
}
}Global parameter expression rewrite
Before (Synapse): @pipeline().globalParameters.batchDate
After (Fabric): @pipeline().libraryVariables.batchDateDataset inlining: Copy activity — dataset reference → connection display name
Synapse dataset InputBlobDataset (will be inlined and removed):
{
"type": "AzureBlob",
"linkedServiceName": {"referenceName": "AzureBlobLinkedService"},
"typeProperties": {"folderPath": "input/", "fileName": "data.csv"}
}Synapse Copy activity (before — references dataset by name):
{
"name": "CopyData", "type": "Copy",
"inputs": [{"referenceName": "InputBlobDataset", "type": "DatasetReference"}],
"typeProperties": {"source": {"type": "BlobSource"}, "sink": {"type": "BlobSink"}}
}After migration (connection display name for AzureBlobLinkedService is My ADLS Connection):
{
"name": "CopyData", "type": "Copy",
"typeProperties": {
"source": {"type": "BlobSource", "storeSettings": {"type": "AzureBlobStorageReadSettings"}},
"sink": {"type": "BlobSink", "storeSettings": {"type": "AzureBlobStorageWriteSettings"}}
},
"linkedService": {"referenceName": "My ADLS Connection", "type": "LinkedServiceReference"}
}See activity-mapping.md and notebook-activity-migration.md for full before/after examples.
Files
- ATTRIBUTION.mdAdded by AutoGineer881 B
- LICENSELicence file added by AutoGineer1.1 KiB
- SKILL.md21.3 KiB
- apm.yml450 B
- resources/activity-mapping.md16.2 KiB
- resources/dataset-inlining.md28.1 KiB
- resources/global-parameters-to-variable-library.md18.7 KiB
- resources/linked-service-to-connection.md16.0 KiB
- resources/migration-report.md16.3 KiB
- resources/notebook-activity-migration.md21.3 KiB
- resources/pipeline-assessment.md35.3 KiB
- resources/pipeline-gotchas.md16.3 KiB
- resources/pipeline-orchestrator.md44.7 KiB
- resources/validation-testing.md19.7 KiB
resources/dataset-inlining.md
Dataset Inlining — Synapse Datasets → Fabric Inline Activity Properties
Synapse pipelines reference datasets as separate workspace items. Fabric Data Factory has no standalone Dataset items — dataset properties are declared inline inside each activity's typeProperties (source, sink, or dataset block).
This file explains how to extract dataset properties from Synapse and embed them directly into pipeline activity JSON.
Why Inlining Is Required
| Synapse | Fabric |
|---|---|
| Dataset is a separate JSON resource with its own name | No Dataset item type in Fabric |
Activity references dataset by referenceName | Activity carries all connector settings inline |
| Dataset can be reused across multiple activities | Each activity carries its own copy of the settings |
If a Synapse dataset is reused by 5 Copy activities, you must inline its properties into all 5 activities in Fabric.
How to Get Dataset Definitions from Synapse
import requests
SYNAPSE_TOKEN = "<synapse-data-plane-token>"
SYNAPSE_ENDPOINT = "https://myworkspace.dev.azuresynapse.net"
headers = {"Authorization": f"Bearer {SYNAPSE_TOKEN}"}
def get_all_datasets():
url = f"{SYNAPSE_ENDPOINT}/datasets?api-version=2020-12-01"
items = []
while url:
r = requests.get(url, headers=headers)
r.raise_for_status()
data = r.json()
items.extend(data.get("value", []))
url = data.get("nextLink")
return items
def build_dataset_map(datasets: list) -> dict:
"""Return {datasetName: datasetProperties} for lookup during inlining."""
return {d["name"]: d["properties"] for d in datasets}
datasets = get_all_datasets()
dataset_map = build_dataset_map(datasets)Activity → Dataset Slot Mapping
Different activity types reference datasets in different slots:
| Activity Type | Dataset Slot | typeProperties Key |
|---|---|---|
Copy (source) | Source dataset | source.storeSettings + source.formatSettings |
Copy (sink) | Sink dataset | sink.storeSettings + sink.formatSettings |
Lookup | Dataset | dataset |
GetMetadata | Dataset | dataset (or inlined storeSettings) |
Delete | Dataset | dataset |
Inlining Patterns by Connector
Parquet on ADLS Gen2
Synapse Dataset (properties):
{
"type": "Parquet",
"linkedServiceName": {
"referenceName": "AzureDataLakeStorage1",
"type": "LinkedServiceReference"
},
"typeProperties": {
"location": {
"type": "AzureBlobFSLocation",
"fileSystem": "raw",
"folderPath": "sales/orders",
"fileName": "orders.parquet"
},
"compressionCodec": "snappy"
}
}Fabric Copy Activity — Source inline:
{
"source": {
"type": "ParquetSource",
"storeSettings": {
"type": "AzureBlobFSReadSettings",
"recursive": false
}
}
}Fabric Copy Activity — fully inlined (connection and location in-place). Note that linkedService is a root-level activity property, a sibling of typeProperties (which contains source/sink) — not nested inside source:
{
"name": "CopyParquet",
"type": "Copy",
"typeProperties": {
"source": {
"type": "ParquetSource",
"storeSettings": {
"type": "AzureBlobFSReadSettings",
"recursive": false,
"fileSystem": "raw",
"folderPath": "path/to/data"
}
},
"sink": { "type": "ParquetSink" }
},
"linkedService": {
"referenceName": "AzureDataLakeStorage1",
"type": "LinkedServiceReference"
}
}Fabric Data Factory has no Dataset item type. The
inputs/outputsarrays andDatasetReferenceobjects from Synapse are removed entirely during inlining — connection and location settings move inline into the activity.
The
linkedServiceName.referenceNamefrom the Synapse dataset becomes the Fabric connection name in the activity'slinkedServiceor dataset block.
CSV / DelimitedText on Azure Blob Storage
Synapse Dataset:
{
"type": "DelimitedText",
"linkedServiceName": {
"referenceName": "AzureBlobStorage1",
"type": "LinkedServiceReference"
},
"typeProperties": {
"location": {
"type": "AzureBlobStorageLocation",
"container": "landing",
"folderPath": "incoming",
"fileName": "data.csv"
},
"columnDelimiter": ",",
"rowDelimiter": "\n",
"quoteChar": "\"",
"escapeChar": "\\",
"firstRowAsHeader": true,
"nullValue": "NULL"
}
}Fabric Copy Source inline:
{
"source": {
"type": "DelimitedTextSource",
"storeSettings": {
"type": "AzureBlobStorageReadSettings",
"recursive": false
},
"formatSettings": {
"type": "DelimitedTextReadSettings"
}
}
}Azure SQL Table
Synapse Dataset:
{
"type": "AzureSqlTable",
"linkedServiceName": {
"referenceName": "AzureSqlDB_Sales",
"type": "LinkedServiceReference"
},
"typeProperties": {
"schema": "dbo",
"table": "Orders"
}
}Fabric Copy Sink inline:
{
"sink": {
"type": "AzureSqlSink",
"writeBehavior": "upsert",
"upsertSettings": {
"useTempDB": true,
"keys": ["OrderId"]
},
"tableOption": "autoCreate",
"disableMetricsCollection": false
}
}Delta Lake on ADLS Gen2
Synapse Dataset:
{
"type": "Parquet",
"linkedServiceName": {
"referenceName": "AzureDataLakeStorage1",
"type": "LinkedServiceReference"
},
"typeProperties": {
"location": {
"type": "AzureBlobFSLocation",
"fileSystem": "silver",
"folderPath": "products"
}
}
}Fabric Copy Sink (Delta):
{
"sink": {
"type": "ParquetSink",
"storeSettings": {
"type": "AzureBlobFSWriteSettings"
},
"formatSettings": {
"type": "ParquetWriteSettings"
}
}
}For true Delta Lake tables in Fabric, consider writing via a
TridentNotebookactivity usingdelta.write()— it gives full ACID semantics including schema evolution, merge, and table history.
Binary (File Copy — no format parsing)
{
"source": {
"type": "BinarySource",
"storeSettings": {
"type": "AzureBlobFSReadSettings",
"recursive": true,
"deleteFilesAfterCompletion": false
}
},
"sink": {
"type": "BinarySink",
"storeSettings": {
"type": "AzureBlobFSWriteSettings"
}
}
}Python Inlining Helper
The following Python function resolves a dataset reference and inlines the necessary connector settings into a Copy/Lookup/GetMetadata/Delete activity:
import copy, json
# Dataset type → source/sink activity type mapping
DATASET_TYPE_TO_SOURCE = {
"Parquet": "ParquetSource",
"DelimitedText": "DelimitedTextSource",
"Json": "JsonSource",
"Avro": "AvroSource",
"Orc": "OrcSource",
"Excel": "ExcelSource",
"AzureSqlTable": "AzureSqlSource",
"SqlServerTable": "SqlServerSource",
"AzureMySqlTable": "AzureMySqlSource",
"AzurePostgreSqlTable": "AzurePostgreSqlSource",
"CosmosDbSqlApiCollection": "CosmosDbSqlApiSource",
"RestResource": "RestSource",
"HttpFile": "HttpSource",
"Binary": "BinarySource",
}
DATASET_TYPE_TO_SINK = {
"Parquet": "ParquetSink",
"DelimitedText": "DelimitedTextSink",
"Json": "JsonSink",
"Avro": "AvroSink",
"Orc": "OrcSink",
"AzureSqlTable": "AzureSqlSink",
"SqlServerTable": "SqlServerSink",
"AzureMySqlTable": "AzureMySqlSink",
"AzurePostgreSqlTable": "AzurePostgreSqlSink",
"CosmosDbSqlApiCollection": "CosmosDbSqlApiSink",
"Binary": "BinarySink",
}
# Tabular (non-file) dataset types — these do not have a `location` /
# `location_type` / `storeSettings`; instead, table identity + query live
# directly under the activity's source/sink (e.g., tableName, schema, query).
TABULAR_DATASET_TYPES = {
"AzureSqlTable",
"SqlServerTable",
"AzureMySqlTable",
"AzurePostgreSqlTable",
"CosmosDbSqlApiCollection",
}
# Fields copied straight through from a tabular dataset's typeProperties
# into the activity's source/sink block.
TABULAR_PASSTHROUGH_FIELDS = ("tableName", "schema", "table", "collectionName", "query")
ADLS_LOCATION_TYPES = {
"AzureBlobFSLocation": ("AzureBlobFSReadSettings", "AzureBlobFSWriteSettings"),
"AzureBlobStorageLocation": ("AzureBlobStorageReadSettings", "AzureBlobStorageWriteSettings"),
"AzureDataLakeStoreLocation": ("AzureDataLakeStoreReadSettings", "AzureDataLakeStoreWriteSettings"),
}
# Fallback mapping from Synapse dataset top-level `type` to a Fabric location
# type, used when the flat-schema dataset (e.g., AzureBlob with folderPath/
# fileName) does not carry a nested `typeProperties.location.type`.
DATASET_TYPE_TO_LOCATION_TYPE = {
"AzureBlob": "AzureBlobStorageLocation",
"AzureBlobFSFile": "AzureBlobFSLocation",
"AzureDataLakeStoreFile": "AzureDataLakeStoreLocation",
"DelimitedText": "AzureBlobStorageLocation",
"Json": "AzureBlobStorageLocation",
"Parquet": "AzureBlobStorageLocation",
"Avro": "AzureBlobStorageLocation",
"Orc": "AzureBlobStorageLocation",
"Binary": "AzureBlobStorageLocation",
}
# Dataset type → formatSettings.type for Lookup/GetMetadata/Delete activities.
# Storage-level types (AzureBlob, AzureBlobFS, etc.) are intentionally absent:
# they use storeSettings only and do not require formatSettings.
# Binary is included because it is a format type (binary file payload) even
# though it has no schema — it pairs with BinaryReadSettings on GetMetadata,
# Lookup, and Delete activities (see the Validation→GetMetadata example in
# activity-mapping.md).
DATASET_TYPE_TO_FORMAT_SETTINGS = {
"DelimitedText": "DelimitedTextReadSettings",
"Json": "JsonReadSettings",
"Parquet": "ParquetReadSettings",
"Avro": "AvroReadSettings",
"Orc": "OrcReadSettings",
"Excel": "ExcelReadSettings",
"Xml": "XmlReadSettings",
"Binary": "BinaryReadSettings",
}
def inline_dataset(activity: dict, dataset_map: dict, connection_map: dict) -> dict:
"""
Inline dataset references in a Copy, Lookup, GetMetadata, or Delete activity.
Args:
activity: The Synapse activity dict (will be deep-copied).
dataset_map: {datasetName: datasetProperties} from Synapse.
connection_map: {linkedServiceName: fabricConnectionName}
Returns:
Activity with dataset references inlined.
"""
activity = copy.deepcopy(activity)
activity_type = activity.get("type")
props = activity.get("typeProperties", {})
if activity_type == "Copy":
# Synapse Copy activities reference one source dataset (inputs[0]) and
# one sink dataset (outputs[0]). Validate that assumption up front so
# multi-input/output configurations don't silently lose dataset
# references when only the first entry gets inlined.
inputs = activity.get("inputs", [])
outputs = activity.get("outputs", [])
if len(inputs) > 1 or len(outputs) > 1:
raise ValueError(
f"Copy activity '{activity.get('name')}' has {len(inputs)} input(s) "
f"and {len(outputs)} output(s); only single-input / single-output "
f"Copy activities are supported by this inlining helper. Split the "
f"activity in Synapse before re-running Phase 3."
)
# Inline source dataset from Synapse inputs[0] (DatasetReference)
src_conn = None
if inputs:
src_ds_name = inputs[0].get("referenceName")
if src_ds_name:
if src_ds_name not in dataset_map:
raise KeyError(
f"Activity '{activity.get('name')}' references unknown source "
f"dataset '{src_ds_name}'. Add it to dataset_map (Phase 3 "
f"dataset inventory) or remove the reference."
)
src_conn = _inline_copy_source(props, src_ds_name, dataset_map[src_ds_name], connection_map)
activity.pop("inputs", None)
# Inline sink dataset
sink_conn = None
if outputs:
sink_ds_name = outputs[0].get("referenceName")
if sink_ds_name:
if sink_ds_name not in dataset_map:
raise KeyError(
f"Activity '{activity.get('name')}' references unknown sink "
f"dataset '{sink_ds_name}'. Add it to dataset_map (Phase 3 "
f"dataset inventory) or remove the reference."
)
sink_conn = _inline_copy_sink(props, sink_ds_name, dataset_map[sink_ds_name], connection_map)
activity.pop("outputs", None)
# Emit connection references at the activity root level — linkedService is a
# sibling of typeProperties per the Fabric Data Pipeline schema (see SKILL.md).
# Same-system copy: one activity["linkedService"] covers both source and sink.
# Cross-system copy: source connection at the activity root; sink connection
# embedded inside the sink block so both identities are preserved in the JSON.
if src_conn and sink_conn and src_conn != sink_conn:
activity["linkedService"] = {"referenceName": src_conn, "type": "LinkedServiceReference"}
props.setdefault("sink", {})["linkedService"] = {
"referenceName": sink_conn, "type": "LinkedServiceReference",
}
elif src_conn or sink_conn:
activity["linkedService"] = {
"referenceName": src_conn or sink_conn, "type": "LinkedServiceReference",
}
elif activity_type in ("Lookup", "GetMetadata", "Delete"):
ds_ref = props.get("dataset", {})
ds_name = ds_ref.get("referenceName")
if ds_name:
if ds_name not in dataset_map:
raise KeyError(
f"Activity '{activity.get('name')}' references unknown dataset "
f"'{ds_name}'. Add it to dataset_map (Phase 3 dataset inventory) "
f"or remove the reference."
)
ds_props = dataset_map[ds_name]
# Pop the original dataset reference up-front so the helper can
# write a new typed `dataset` block (for tabular sources) without
# being clobbered by an unconditional pop after the call.
props.pop("dataset", None)
conn = _inline_dataset_reference(props, ds_name, ds_props, connection_map)
if conn:
activity["linkedService"] = {"referenceName": conn, "type": "LinkedServiceReference"}
activity["typeProperties"] = props
return activity
def _get_read_settings_type(location_type: str, dataset_name: str = "<unknown>", dataset_type: str = "<unknown>") -> str:
mapped = ADLS_LOCATION_TYPES.get(location_type)
if mapped is None:
raise ValueError(
f"Unsupported location_type '{location_type}' for dataset "
f"'{dataset_name}' (type='{dataset_type}'). "
f"Supported: {sorted(ADLS_LOCATION_TYPES.keys())}."
)
return mapped[0]
def _get_write_settings_type(location_type: str, dataset_name: str = "<unknown>", dataset_type: str = "<unknown>") -> str:
mapped = ADLS_LOCATION_TYPES.get(location_type)
if mapped is None:
raise ValueError(
f"Unsupported location_type '{location_type}' for dataset "
f"'{dataset_name}' (type='{dataset_type}'). "
f"Supported: {sorted(ADLS_LOCATION_TYPES.keys())}."
)
return mapped[1]
def _normalize_location(ds_type_props: dict) -> dict:
"""Return a unified location dict regardless of dataset schema:
- Nested schema: typeProperties.location.{type, fileSystem, folderPath, …}
(e.g., AzureBlobFSFile, AzureDataLakeStoreFile)
- Flat schema: typeProperties.{folderPath, fileName, …} at top level
(e.g., AzureBlob, AzureDataLakeStore v1)
"""
nested = ds_type_props.get("location")
if nested:
return nested
# Flat schema — path fields sit directly under typeProperties
return {k: ds_type_props[k]
for k in ("type", "fileSystem", "container", "folderPath", "fileName")
if k in ds_type_props}
def _inline_copy_source(props: dict, ds_name: str, ds_props: dict, connection_map: dict) -> str:
ds_type = ds_props.get("type")
if not ds_type:
# Fail fast: an empty/missing dataset `type` would produce an invalid
# "NoneSource" / "Source" identifier that later trips schema validation
# in non-obvious ways downstream.
raise ValueError(
f"Dataset '{ds_name}' has no `type` field — cannot infer Fabric "
f"source type. Fix the dataset definition in Synapse or add a "
f"mapping override for it before re-running Phase 3."
)
linked_svc = ds_props.get("linkedServiceName", {}).get("referenceName", "")
fabric_conn = connection_map.get(linked_svc, linked_svc) # fall back to same name
source = props.setdefault("source", {})
# Resolve the source activity "type":
# 1. Use the explicit mapping when one exists (e.g. Parquet → ParquetSource).
# 2. Otherwise preserve any pre-existing source.type the activity already
# declared — storage-only Synapse datasets (AzureBlob, AzureBlobFSFile,
# AzureDataLakeStoreFile) carry no file-format info, and the activity is
# where the Synapse author chose the right reader (e.g. ParquetSource,
# DelimitedTextSource, BinarySource). Synthesizing "AzureBlobSource"
# from the dataset type would be schema-invalid in Fabric / ADF.
# 3. If neither path produces a valid type, fail fast with a clear message
# instead of silently writing an invalid identifier.
mapped = DATASET_TYPE_TO_SOURCE.get(ds_type)
if mapped:
source["type"] = mapped
elif source.get("type"):
# Preserve the existing activity source.type (e.g. ParquetSource).
pass
else:
raise ValueError(
f"Cannot infer Fabric Copy source type for dataset '{ds_name}' "
f"(dataset type='{ds_type}'). Storage-only dataset types (AzureBlob, "
f"AzureBlobFSFile, AzureDataLakeStoreFile, …) carry no file-format "
f"information — set `source.type` on the activity in Synapse (e.g. "
f"`ParquetSource`, `DelimitedTextSource`, `BinarySource`) or add an "
f"entry to DATASET_TYPE_TO_SOURCE before re-running Phase 3."
)
if ds_type in TABULAR_DATASET_TYPES:
# Tabular connectors: no storeSettings; copy table identity/query through.
ds_tp = ds_props.get("typeProperties", {}) or {}
for key in TABULAR_PASSTHROUGH_FIELDS:
if key in ds_tp:
source[key] = ds_tp[key]
return fabric_conn
location = _normalize_location(ds_props.get("typeProperties", {}))
# Flat-schema datasets (AzureBlob with folderPath/fileName) carry no
# location.type — fall back to the dataset type.
location_type = location.get("type") or DATASET_TYPE_TO_LOCATION_TYPE.get(ds_type, "")
# Inline location fields (fileSystem/container, folderPath, fileName) into storeSettings
store = source.setdefault("storeSettings", {})
store["type"] = _get_read_settings_type(location_type, ds_name or "<unknown>", ds_type or "<unknown>")
for key in ("fileSystem", "container", "folderPath", "fileName"):
if key in location:
store[key] = location[key]
return fabric_conn
def _inline_copy_sink(props: dict, ds_name: str, ds_props: dict, connection_map: dict) -> str:
ds_type = ds_props.get("type")
if not ds_type:
# Fail fast: an empty/missing dataset `type` would produce an invalid
# "NoneSink" / "Sink" identifier that later trips schema validation
# in non-obvious ways downstream.
raise ValueError(
f"Dataset '{ds_name}' has no `type` field — cannot infer Fabric "
f"sink type. Fix the dataset definition in Synapse or add a "
f"mapping override for it before re-running Phase 3."
)
linked_svc = ds_props.get("linkedServiceName", {}).get("referenceName", "")
fabric_conn = connection_map.get(linked_svc, linked_svc)
sink = props.setdefault("sink", {})
# See _inline_copy_source for the resolution rules; symmetric handling on the sink.
mapped = DATASET_TYPE_TO_SINK.get(ds_type)
if mapped:
sink["type"] = mapped
elif sink.get("type"):
# Preserve the existing activity sink.type (e.g. ParquetSink).
pass
else:
raise ValueError(
f"Cannot infer Fabric Copy sink type for dataset '{ds_name}' "
f"(dataset type='{ds_type}'). Storage-only dataset types (AzureBlob, "
f"AzureBlobFSFile, AzureDataLakeStoreFile, …) carry no file-format "
f"information — set `sink.type` on the activity in Synapse (e.g. "
f"`ParquetSink`, `DelimitedTextSink`, `BinarySink`) or add an entry "
f"to DATASET_TYPE_TO_SINK before re-running Phase 3."
)
if ds_type in TABULAR_DATASET_TYPES:
ds_tp = ds_props.get("typeProperties", {}) or {}
for key in TABULAR_PASSTHROUGH_FIELDS:
if key in ds_tp:
sink[key] = ds_tp[key]
return fabric_conn
location = _normalize_location(ds_props.get("typeProperties", {}))
location_type = location.get("type") or DATASET_TYPE_TO_LOCATION_TYPE.get(ds_type, "")
# Inline location fields (fileSystem/container, folderPath, fileName) into storeSettings
store = sink.setdefault("storeSettings", {})
store["type"] = _get_write_settings_type(location_type, ds_name or "<unknown>", ds_type or "<unknown>")
for key in ("fileSystem", "container", "folderPath", "fileName"):
if key in location:
store[key] = location[key]
return fabric_conn
def _inline_dataset_reference(props: dict, ds_name: str, ds_props: dict, connection_map: dict) -> str:
"""For Lookup, GetMetadata, Delete — inline the dataset reference."""
ds_type = ds_props.get("type")
linked_svc = ds_props.get("linkedServiceName", {}).get("referenceName", "")
fabric_conn = connection_map.get(linked_svc, linked_svc)
if ds_type in TABULAR_DATASET_TYPES:
# Tabular Lookup/GetMetadata: emit a typed dataset block instead of storeSettings.
ds_tp = ds_props.get("typeProperties", {}) or {}
dataset_block = {"type": ds_type}
for key in TABULAR_PASSTHROUGH_FIELDS:
if key in ds_tp:
dataset_block[key] = ds_tp[key]
props["dataset"] = dataset_block
return fabric_conn
location = _normalize_location(ds_props.get("typeProperties", {}))
location_type = location.get("type") or DATASET_TYPE_TO_LOCATION_TYPE.get(ds_type, "")
store = {"type": _get_read_settings_type(location_type, ds_name or "<unknown>", ds_type or "<unknown>")}
# Carry over location fields so the activity can still locate the data
# after the dataset reference is removed (matches _inline_copy_source pattern)
for key in ("fileSystem", "container", "folderPath", "fileName"):
if key in location:
store[key] = location[key]
props["storeSettings"] = store
# Carry over formatSettings only when we know the concrete type;
# emitting an empty {} is invalid schema for most activity types.
fmt_type = DATASET_TYPE_TO_FORMAT_SETTINGS.get(ds_type)
if fmt_type:
props["formatSettings"] = {"type": fmt_type}
return fabric_conn
def inline_all_activities(
activities: list,
dataset_map: dict,
connection_map: dict,
) -> tuple[list, list[str]]:
"""
Inline datasets in all activities, including nested inner activities.
Returns:
(transformed_activities, list_of_warning_messages)
"""
warnings = []
result = []
for activity in activities:
try:
activity = inline_dataset(activity, dataset_map, connection_map)
except Exception as e:
warnings.append(f"Activity '{activity.get('name')}': {e}")
# Recurse into containers
for key in ("activities", "ifTrueActivities", "ifFalseActivities", "defaultActivities"):
if key in activity.get("typeProperties", {}):
inner, inner_w = inline_all_activities(
activity["typeProperties"][key], dataset_map, connection_map
)
activity["typeProperties"][key] = inner
warnings.extend(inner_w)
if activity.get("type") == "Switch":
for case in activity.get("typeProperties", {}).get("cases", []):
inner, inner_w = inline_all_activities(
case.get("activities", []), dataset_map, connection_map
)
case["activities"] = inner
warnings.extend(inner_w)
result.append(activity)
return result, warningsDataset Parameters
Some Synapse datasets use parameters (e.g., dynamic file paths). When inlining, incorporate the parameter values into the activity's typeProperties directly or via pipeline expression:
Synapse Dataset with parameter:
{
"type": "Parquet",
"parameters": {
"folderPath": { "type": "String" }
},
"typeProperties": {
"location": {
"type": "AzureBlobFSLocation",
"fileSystem": "raw",
"folderPath": {
"value": "@dataset().folderPath",
"type": "Expression"
}
}
}
}Fabric — Inline with pipeline expression:
{
"source": {
"type": "ParquetSource",
"storeSettings": {
"type": "AzureBlobFSReadSettings",
"recursive": false,
"wildcardFolderPath": {
"value": "@pipeline().parameters.folderPath",
"type": "Expression"
}
}
}
}Replace
@dataset().paramNamewith the actual pipeline parameter or expression that the activity was passing to the dataset at runtime.
Connection Reference Placement in Fabric Data Pipeline JSON
In Fabric Data Pipeline JSON, linkedService is a root-level property of the activity — a sibling of typeProperties, not nested inside source or sink:
{
"name": "CopyData",
"type": "Copy",
"typeProperties": {
"source": { "type": "ParquetSource", "storeSettings": { "..." : "..." } },
"sink": { "type": "ParquetSink", "storeSettings": { "..." : "..." } }
},
"linkedService": { "referenceName": "MyADLSConnection", "type": "LinkedServiceReference" }
}The same rule applies to GetMetadata, Lookup, and Delete activities.
Same-System Copy (source and sink share one connection)
A single activity.linkedService covers both sides:
{
"name": "CopyBlobToBlob",
"type": "Copy",
"typeProperties": {
"source": { "type": "ParquetSource", "storeSettings": { "type": "AzureBlobFSReadSettings" } },
"sink": { "type": "ParquetSink", "storeSettings": { "type": "AzureBlobFSWriteSettings" } }
},
"linkedService": { "referenceName": "ADLSGen2Connection", "type": "LinkedServiceReference" }
}Cross-System Copy (source and sink use different connections)
The source connection goes to the activity root (activity.linkedService). The sink connection is embedded directly inside the sink block, since only one root-level linkedService key exists:
{
"name": "CopySqlToParquet",
"type": "Copy",
"typeProperties": {
"source": {
"type": "AzureSqlSource",
"queryTimeout": "02:00:00"
},
"sink": {
"type": "ParquetSink",
"storeSettings": { "type": "AzureBlobFSWriteSettings" },
"linkedService": { "referenceName": "ADLSGen2Connection", "type": "LinkedServiceReference" }
}
},
"linkedService": { "referenceName": "AzureSqlConnection", "type": "LinkedServiceReference" }
}The Python inlining helper (inline_dataset) applies this pattern automatically: when source and sink resolve to the same Fabric connection name a single root key is emitted; when they differ the source connection goes to activity["linkedService"] and the sink connection is written into typeProperties.sink.linkedService.
Requirements and compatibility
Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta
“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.
Security
No issues found by our static scan· No issues found
A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.
Attribution and licence
- Published by
- Microsoft
- Publisher
- Microsoft (Official)
- Source
- microsoft/skills-for-fabric · skills/pipeline-migration · commit f8fac11
- Licence
- MIT License (redistribution allowed)
- LICENSE (added by AutoGineer from LICENSE)
- ATTRIBUTION.md (written by AutoGineer)
- Synced
- Also distributed in
- microsoft/skills-for-fabric
AutoGineer is not affiliated with or endorsed by Microsoft or Microsoft.
Download ZIP
The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download pipeline-migration.zip