This toolset automatesthe conversion of Microsoft Fabric Dataflow Gen2 exports into portable Power Query Template (.pqt) files.
This repository contains scripts for processing Fabric Dataflow exports:
process_dataflows.py - Unified interface for complete workflow
batch_decode_dataflows.py- Decodes base64-encoded Fabric dataflow exportscreate_pqt_from_workspace.py- Converts decoded dataflows into .pqt template files
Fabric Export (JSON) → process_dataflows.py --all → Decoded Files + .pqt Templates
Or run individually:
Fabric Export (JSON) → batch_decode_dataflows.py → Decoded Files → create_pqt_from_workspace.py → .pqt Templates
- Python 3.6+ (minimum)
- Python 3.8+ (recommended for best compatibility and performance)
- Tested on: Python 3.13
- No external dependencies (uses only standard library)
# Complete workflow (decode + convert)
python process_dataflows.py --all <source_directory>
# Decode only
python process_dataflows.py --decode <source_directory>
# Convert only (already decoded)
python process_dataflows.py --convert <source_directory>
# With custom output directory
python process_dataflows.py --all <source_directory> --output <output_directory># Run complete workflow on Fabric exports
python process_dataflows.py --all "PIE_WORKSPACES"
# Decode exports only
python process_dataflows.py --decode "PIE_WORKSPACES"
# Convert already-decoded dataflows to .pqt
python process_dataflows.py --convert "PIE_WORKSPACES"
# Complete workflow with custom output
python process_dataflows.py --all "PIE_WORKSPACES" --output "output/templates"python process_dataflows.py --helpDecodes base64-encoded Fabric Dataflow Gen2 export files into readable formats.
python batch_decode_dataflows.py <source_directory>Examples:
python batch_decode_dataflows.py "PIE_WORKSPACES"
python batch_decode_dataflows.py "C:\Fabric\Exports\Dataflows"- Scans the source directory for
.jsonfiles (Fabric exports) - Creates numbered subdirectories (
item_001,item_002, etc.) to avoid Windows MAX_PATH issues - Decodes base64 payloads from each export file
- Extracts individual components:
mashup.pq- Power Query M codequeryMetadata.json- Query metadata.platform- Fabric platform configuration- Other JSON/text files
- Saves
definition_decoded.jsonwith all decoded content - Moves original file into the subdirectory
- Creates
item_mapping.txtto track which item corresponds to which original file
Fabric dataflow export files with this structure:
{
"definition": {
"parts": [
{
"path": "mashup.pq",
"payload": "base64_encoded_content...",
"payloadType": "InlineBase64"
},
...
]
}
}source_directory/
├── item_001/
│ ├── mashup.pq # Decoded M code
│ ├── queryMetadata.json # Decoded metadata
│ ├── .platform # Decoded config
│ ├── definition_decoded.json # Complete decoded definition
│ └── WS__original_export.json # Original file (moved here)
├── item_002/
│ └── ...
└── item_mapping.txt # Maps item_XXX to original filenames
Includes workspace ID, item ID, name, type, and original filename:
item_001 | WorkspaceID: aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee | ItemID: 11111111-2222-3333-4444-555555555555 | Name: DataflowName | Type: Dataflow | File: WS__aaaaaaaa-bbbb-cccc__11111111-2222-3333__DataflowName__Dataflow.json
item_002 | WorkspaceID: ffffffff-gggg-hhhh-iiii-jjjjjjjjjjjj | ItemID: 66666666-7777-8888-9999-aaaaaaaaaaaa | Name: FinanceRun | Type: Dataflow | File: WS__ffffffff-gggg-hhhh__66666666-7777-8888__FinanceRun__Dataflow.json
Converts decoded dataflow directories into Power Query Template (.pqt) files for import into Power BI or Excel.
Basic (works in-place, no copying):
python create_pqt_from_workspace.py <source_workspace_path>This creates .pqt files and with_dataflows/ directory directly in the source directory.
Custom output location:
python create_pqt_from_workspace.py <source_workspace_path> <output_directory>This copies items to a new location before processing.
Examples:
# Work in-place (default)
python create_pqt_from_workspace.py "PIE_WORKSPACES_decoded"
# Copy to custom output location
python create_pqt_from_workspace.py "PIE_WORKSPACES_decoded" "output/pie_templates"- Identifies dataflow items (directories named
item_XXXcontainingmashup.pq) - Works in-place by default (or copies to output directory if specified)
- Creates
pqtzipdirectory with transformed files:MashupDocument.pq(copy ofmashup.pq)MashupMetadata.json(transformed fromqueryMetadata.json)Metadata.json(extracted from.platform)[Content_Types].xml(generated XML manifest)
- Creates
.pqtZIP archives - Moves items with
.pqtfiles towith_dataflows/subdirectory - Generates
item_mapping.txtfiles:- In
with_dataflows/- for items with dataflows (have .pqt files) - In main directory - for items without dataflows (no .pq files)
- In
By default, the script works in-place and organizes the source directory:
source_workspace/ # Your original decoded workspace
├── with_dataflows/ # Items with .pqt files (have mashup.pq)
│ ├── item_001/
│ │ ├── mashup.pq # Original M query file
│ │ ├── queryMetadata.json # Original metadata
│ │ ├── .platform # Original platform config
│ │ ├── WS__10ee8e5c...json # Original workspace file
│ │ ├── pqtzip/ # Template structure
│ │ │ ├── MashupDocument.pq # Copy of mashup.pq
│ │ │ ├── MashupMetadata.json # Transformed metadata
│ │ │ ├── Metadata.json # Name/version info
│ │ │ └── [Content_Types].xml # MIME type declarations
│ │ └── item_001.pqt # Final ZIP archive
│ ├── item_002/
│ │ └── ...
│ └── item_mapping.txt # Mapping for items with dataflows
├── item_003/ # Items without .pq files remain here
│ └── ...
└── item_mapping.txt # Mapping for items without dataflows (if any)
Both scripts generate the same format with complete metadata:
item_001 | WorkspaceID: aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee | ItemID: 11111111-2222-3333-4444-555555555555 | Name: DataflowTest | Type: Dataflow | File: WS__aaaaaaaa-bbbb-cccc__11111111-2222-3333__DataflowTest__Dataflow.json
item_002 | WorkspaceID: ffffffff-gggg-hhhh-iiii-jjjjjjjjjjjj | ItemID: 66666666-7777-8888-9999-aaaaaaaaaaaa | Name: FinanceRun | Type: Dataflow | File: WS__ffffffff-gggg-hhhh__66666666-7777-8888__FinanceRun__Dataflow.json
Each entry includes:
- Item Directory: Local directory name (
item_XXX) - WorkspaceID: Fabric workspace GUID
- ItemID: Fabric item GUID
- Name: Display name of the item
- Type: Item type (typically "Dataflow")
- File: Original export filename
cd C:\Fabric\Exports
python process_dataflows.py --all "PIE_WORKSPACES"This single command will:
- Decode all base64-encoded export files
- Create .pqt templates
- Organize output into
with_dataflows/directory
Output:
======================================================================
STEP 1: DECODING DATAFLOW EXPORTS
======================================================================
BATCH DECODING DATAFLOW DEFINITIONS
Source Directory: PIE_WORKSPACES
Found 45 JSON files
...
✅ Successful: 45
======================================================================
STEP 2: CONVERTING TO .PQT FILES
======================================================================
Processing workspace: C:\Fabric\Exports\PIE_WORKSPACES
...
Successfully created 45/45 .pqt files
Successfully moved 45/45 items to with_dataflows/
======================================================================
WORKFLOW SUMMARY
======================================================================
Operation: Complete workflow (decode + convert)
Source directory: PIE_WORKSPACES
✅ All operations completed successfully
If you prefer to run steps separately:
Export your dataflows using the Fabric REST API. You'll get JSON files with base64-encoded content.
cd C:\Fabric\Exports
python batch_decode_dataflows.py "PIE_WORKSPACES"Output:
======================================================================
BATCH DECODING DATAFLOW DEFINITIONS
======================================================================
Source Directory: PIE_WORKSPACES
Found 45 JSON files
[1/45] Processing: WS__10ee8e5c__2523106f__DataflowTest__Dataflow.json
✅ Decoded to: PIE_WORKSPACES\item_001
✅ Original file moved to subdirectory
[2/45] Processing: WS__22b3514d__5fce0e6a__FinanceRun__Dataflow.json
✅ Decoded to: PIE_WORKSPACES\item_002
✅ Original file moved to subdirectory
...
======================================================================
BATCH DECODE COMPLETE
======================================================================
✅ Successful: 45
❌ Failed: 0
💡 See 'item_mapping.txt' for file-to-folder mapping
======================================================================
python create_pqt_from_workspace.py "PIE_WORKSPACES"Output:
======================================================================
Processing workspace: C:\Fabric\Exports\PIE_WORKSPACES
Output directory: C:\Fabric\Exports\PIE_WORKSPACES
======================================================================
Found 45 dataflow items in PIE_WORKSPACES
Working in place (source directory)
Creating pqtzip structures...
✓ item_001
✓ item_002
...
✓ item_045
Successfully created 45/45 pqtzip structures
Creating .pqt archive files...
✓ item_001.pqt
✓ item_002.pqt
...
✓ item_045.pqt
Successfully created 45/45 .pqt files
Moving items with .pqt files to with_dataflows...
✓ Moved item_001 to with_dataflows/
✓ Moved item_002 to with_dataflows/
...
✓ Moved item_045 to with_dataflows/
Successfully moved 45/45 items to with_dataflows/
Created item_mapping.txt with 45 entries at with_dataflows\item_mapping.txt
======================================================================
PROCESSING COMPLETE
======================================================================
Total items processed: 45
Items with dataflows (.pq files): 45
- pqtzip structures created: 45
- .pqt archives created: 45
- Moved to with_dataflows/: 45
Items without dataflows: 0
Output location: C:\Fabric\Exports\PIE_WORKSPACES
Dataflows location: C:\Fabric\Exports\PIE_WORKSPACES\with_dataflows
======================================================================
The .pqt conversion produces these files in the pqtzip/ directory:
Transforms queryMetadata.json from Fabric format to Power Query format:
Input (queryMetadata.json):
{
"queriesMetadata": {
"Query1": {"isHidden": false},
"Query2": {"isHidden": true}
}
}Output (MashupMetadata.json):
{
"Version": "1.0.0.0",
"QueriesMetadata": [
{"Name": "Query1", "IsHidden": false},
{"Name": "Query2", "IsHidden": true}
]
}Extracts dataflow name from .platform file:
{
"Name": "Finance_Monthly_Run",
"Description": "",
"Version": "1.0.0.0"
}Standard MIME type declarations for .pqt package:
<?xml version="1.0" encoding="utf-8"?>
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
<Default Extension="json" ContentType="application/json" />
<Default Extension="pq" ContentType="application/x-ms-m" />
</Types>The .pqt files are now in PIE_WORKSPACES\with_dataflows\item_XXX\item_XXX.pqt
Import into Power BI Desktop:
- Open Power BI Desktop
- Home → Get Data → Power Query Template
- Select the
.pqtfile - Update connection parameters as needed
| Flag | Description | Example |
|---|---|---|
--all |
Run complete workflow (decode + convert) | python process_dataflows.py --all "source" |
--decode |
Decode exports only | python process_dataflows.py --decode "source" |
--convert |
Convert to .pqt only | python process_dataflows.py --convert "source" |
--output / -o <dir> |
Custom output directory | python process_dataflows.py --all "source" --output "out" |
--help |
Show help message | python process_dataflows.py --help |
Note:
--outputonly affects the convert step. When using--all, decoded files are always written to the source directory.
batch_decode_dataflows.py:
python batch_decode_dataflows.py <source_directory>create_pqt_from_workspace.py:
# In-place (default)
python create_pqt_from_workspace.py <source_directory>
# Custom output
python create_pqt_from_workspace.py <source_directory> <output_directory>No JSON files found
- Verify the source directory path is correct
- Ensure JSON files are directly in the source directory (not in subdirectories)
- Check file extensions are
.json
Decoding errors
- Ensure files are valid Fabric dataflow exports
- Check file encoding is UTF-8
- Verify JSON structure matches expected format
No dataflow items found
- Ensure workspace was decoded with
batch_decode_dataflows.pyfirst - Verify
item_XXXdirectories containmashup.pqfiles - Check the source path points to the correct decoded directory
Missing files in pqtzip
- Ensure source items have
queryMetadata.jsonand.platformfiles - Re-run
batch_decode_dataflows.pyif files are missing
.pqt archive creation fails
- Verify sufficient disk space
- Check write permissions in output directory
- Ensure no files are open/locked in the source directories
Fabric exports dataflows as JSON files with base64-encoded parts:
{
"definition": {
"parts": [
{"path": "mashup.pq", "payload": "base64...", "payloadType": "InlineBase64"},
{"path": "queryMetadata.json", "payload": "base64...", "payloadType": "InlineBase64"}
]
}
}A .pqt file is a ZIP archive containing exactly 4 files:
MashupDocument.pq- The Power Query M codeMashupMetadata.json- Query metadata (names, visibility)Metadata.json- Template name, description, version[Content_Types].xml- MIME type declarations
The script converts Fabric's nested queriesMetadata object to Power Query's flat QueriesMetadata array, preserving query names and visibility settings.
The tools use short numbered directory names (item_001, item_002) to avoid Windows 260-character path length limitations, especially important when working with deeply nested workspace structures.
- Template Distribution: Create portable .pqt files for sharing dataflows across teams
- Version Control: Store decoded M code in source control systems
- Migration: Move dataflows between Fabric workspaces or tenants
- Backup: Archive dataflow definitions outside of Fabric
- Development: Edit dataflows in external tools before re-importing
Licensed under the MIT License. See LICENSE file for details.