
Fabric Cli Powerbi
- 118 installs
- 163 repo stars
- Updated July 23, 2026
- microsoft/fabric-cli
Fabric CLI skill for Power BI semantic models reports DAX refresh and gateways.
About
Microsoft Fabric CLI skill for Power BI operations. Covers semantic model management, report deployment, DAX query execution, dataset refresh scheduling, and gateway configuration through fabric CLI commands. Activates when users work with Power BI items, refresh datasets, execute DAX, manage reports, or troubleshoot Fabric workspace connectivity. Documents authentication, workspace selection, and command patterns for automating Power BI administration from terminal workflows.
- Fabric CLI for Power BI semantic models and reports
- DAX query execution and dataset refresh operations
- Gateway management and workspace connectivity
- Terminal automation for Power BI administration
- Activates on Power BI items refresh DAX and report management
Fabric Cli Powerbi by the numbers
- 118 all-time installs (skills.sh)
- Ranked #772 of 2,066 Data Science & ML skills by installs in the Skillselion catalog
- Security screen: HIGH risk (skills.sh audit)
- Data as of Jul 28, 2026 (Skillselion catalog sync)
fabric-cli-powerbi capabilities & compatibility
- Capabilities
- execute dax · refresh dataset · manage semantic models · configure gateways
- Works with
- azure · power bi
- Use cases
- data analysis · devops
What fabric-cli-powerbi says it does
Use Fabric CLI for Power BI operations — semantic models, reports, DAX queries, refresh, gateways.
npx skills add https://github.com/microsoft/fabric-cli --skill fabric-cli-powerbiAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 118 |
|---|---|
| repo stars | ★ 163 |
| Security audit | 2 / 3 scanners passed |
| Last updated | July 23, 2026 |
| Repository | microsoft/fabric-cli ↗ |
How do I manage Power BI items from the Fabric CLI?
Use Fabric CLI for Power BI semantic models, reports, DAX queries, dataset refresh, and gateway management.
Who is it for?
Developers automating Power BI semantic models, reports, and dataset refresh.
Skip if: Power Apps code app connector setup or non-Fabric data engineering.
When should I use this skill?
User works with Power BI items, DAX queries, dataset refresh, or gateways via Fabric CLI.
What you get
Power BI operation completed via fabric CLI with correct workspace and auth.
Files
Fabric CLI Power BI Operations
Expert guidance for working with Power BI items (semantic models, reports, dashboards) using the fab CLI.
When to Use This Skill
Activate automatically when tasks involve:
- Semantic model (dataset) operations — get, export, refresh, update
- Report management — export, clone, rebind to different model
- Executing DAX queries against semantic models
- Managing refresh schedules and troubleshooting failures
- Gateway and data source configuration
- TMDL (Tabular Model Definition Language) operations
Prerequisites
- Load
fabric-cli-coreskill first for foundational CLI guidance - User must be authenticated:
fab auth status - Appropriate workspace permissions for target items
Automation Scripts
Ready-to-use Python scripts for Power BI tasks. Run any script with --help for full options.
| Script | Purpose | Usage |
|---|---|---|
refresh_model.py | Trigger and monitor semantic model refresh | python scripts/refresh_model.py <model> [--wait] [--timeout 300] |
list_refresh_history.py | Show refresh history and failure details | python scripts/list_refresh_history.py <model> [--last N] |
rebind_report.py | Rebind report to different semantic model | python scripts/rebind_report.py <report> --model <new-model> |
Scripts are located in the scripts/ folder of this skill.
1 - Power BI Item Types
| Entity Suffix | Type | Description |
|---|---|---|
.SemanticModel | Semantic Model | Power BI dataset (tabular model) |
.Report | Report | Power BI report (visualizations) |
.Dashboard | Dashboard | Power BI dashboard (pinned tiles) |
.Dataflow | Dataflow | Power Query dataflow |
.PaginatedReport | Paginated Report | RDL-based paginated report |
Path Examples
# Semantic model
Production.Workspace/Sales.SemanticModel
# Report connected to model
Production.Workspace/SalesReport.Report
# Dashboard
Production.Workspace/ExecutiveDash.Dashboard2 - Semantic Model Operations
Get Model Information
# Check if model exists
fab exists "ws.Workspace/Model.SemanticModel"
# Get model properties
fab get "ws.Workspace/Model.SemanticModel"
# Get model ID (needed for Power BI API calls)
fab get "ws.Workspace/Model.SemanticModel" -q "id"
# Get full definition (TMDL)
fab get "ws.Workspace/Model.SemanticModel" -q "definition"Export Model
# Export to local directory (PBIP format with TMDL)
fab export "ws.Workspace/Model.SemanticModel" -o ./exports -fCreates folder structure:
Model.SemanticModel/
├── .platform
├── definition.pbism
└── definition/
├── model.tmdl
├── tables/
│ ├── Sales.tmdl
│ └── Date.tmdl
└── relationships.tmdlImport/Update Model
# Import from PBIP folder
fab import "ws.Workspace/Model.SemanticModel" -i ./exports/Model.SemanticModel -f
# Copy between workspaces
fab cp "Dev.Workspace/Model.SemanticModel" "Prod.Workspace/Model.SemanticModel" -f3 - Refresh Operations
Trigger Refresh
# Get IDs
WS_ID=$(fab get "ws.Workspace" -q "id" | tr -d '"')
MODEL_ID=$(fab get "ws.Workspace/Model.SemanticModel" -q "id" | tr -d '"')
# Trigger full refresh
fab api -A powerbi "groups/$WS_ID/datasets/$MODEL_ID/refreshes" -X post -i '{"type":"Full"}'
# Check refresh status
fab api -A powerbi "groups/$WS_ID/datasets/$MODEL_ID/refreshes?\$top=1"Enhanced Refresh (Partition-Level)
# Refresh specific tables
fab api -A powerbi "groups/$WS_ID/datasets/$MODEL_ID/refreshes" -X post -i '{
"type": "Full",
"commitMode": "transactional",
"objects": [
{"table": "Sales"},
{"table": "Inventory"}
]
}'
# Refresh with retry
fab api -A powerbi "groups/$WS_ID/datasets/$MODEL_ID/refreshes" -X post -i '{
"type": "Full",
"retryCount": 3
}'Refresh Schedule
# Get current schedule
fab api -A powerbi "datasets/$MODEL_ID/refreshSchedule"
# Set daily refresh at 6 AM UTC
fab api -A powerbi "datasets/$MODEL_ID/refreshSchedule" -X patch -i '{
"enabled": true,
"days": ["Monday","Tuesday","Wednesday","Thursday","Friday"],
"times": ["06:00"],
"localTimeZoneId": "UTC"
}'Troubleshoot Refresh Failures
# Get refresh history
fab api -A powerbi "groups/$WS_ID/datasets/$MODEL_ID/refreshes"
# Common failure patterns:
# - "credentials" → Update data source credentials
# - "gateway" → Check gateway status
# - "timeout" → Use enhanced refresh with smaller batches
# - "memory" → Optimize model or use incremental refresh4 - DAX Query Execution
Execute DAX queries against semantic models:
MODEL_ID=$(fab get "ws.Workspace/Model.SemanticModel" -q "id" | tr -d '"')
# Simple query
fab api -A powerbi "datasets/$MODEL_ID/executeQueries" -X post -i '{
"queries": [{"query": "EVALUATE VALUES(Date[Year])"}]
}'
# Aggregation query
fab api -A powerbi "datasets/$MODEL_ID/executeQueries" -X post -i '{
"queries": [{
"query": "EVALUATE SUMMARIZECOLUMNS(Date[Year], \"Total\", SUM(Sales[Amount]))"
}]
}'
# TOPN query
fab api -A powerbi "datasets/$MODEL_ID/executeQueries" -X post -i '{
"queries": [{
"query": "EVALUATE TOPN(10, Product, [Total Sales], DESC)"
}]
}'
# Query with parameters
fab api -A powerbi "datasets/$MODEL_ID/executeQueries" -X post -i '{
"queries": [{
"query": "EVALUATE FILTER(Sales, Sales[Year] = @Year)",
"parameters": [{"name": "@Year", "value": "2024"}]
}]
}'5 - Report Operations
Get Report Info
# Check exists
fab exists "ws.Workspace/Report.Report"
# Get properties
fab get "ws.Workspace/Report.Report"
# Get connected model
fab get "ws.Workspace/Report.Report" -q "definition.parts[?contains(path, 'definition.pbir')].payload | [0]"Export Report
# Export to PBIP format
fab export "ws.Workspace/Report.Report" -o ./exports -fClone Report
# Copy within workspace
fab cp "ws.Workspace/Report.Report" "ws.Workspace/ReportCopy.Report" -f
# Copy to another workspace
fab cp "Dev.Workspace/Report.Report" "Prod.Workspace/Report.Report" -fRebind Report to Different Model
WS_ID=$(fab get "ws.Workspace" -q "id" | tr -d '"')
REPORT_ID=$(fab get "ws.Workspace/Report.Report" -q "id" | tr -d '"')
NEW_MODEL_ID=$(fab get "ws.Workspace/NewModel.SemanticModel" -q "id" | tr -d '"')
fab api -A powerbi "groups/$WS_ID/reports/$REPORT_ID/Rebind" -X post -i "{
\"datasetId\": \"$NEW_MODEL_ID\"
}"Export Report to File (PDF/PPTX)
# Export to PDF
fab api -A powerbi "groups/$WS_ID/reports/$REPORT_ID/ExportTo" -X post -i '{
"format": "PDF"
}'
# Poll for completion, then download6 - Gateway Operations
List Gateways
# Tenant-level gateways (hidden entity)
fab ls .gateways
# Get gateway details
fab get ".gateways/MyGateway.Gateway"Data Source Management
GATEWAY_ID=$(fab get ".gateways/MyGateway.Gateway" -q "id" | tr -d '"')
# List data sources on gateway
fab api -A powerbi "gateways/$GATEWAY_ID/datasources"
# Get data source status
fab api -A powerbi "gateways/$GATEWAY_ID/datasources/$DATASOURCE_ID"Update Data Source Credentials
# Update credentials (basic auth example)
fab api -A powerbi "gateways/$GATEWAY_ID/datasources/$DATASOURCE_ID" -X patch -i '{
"credentialDetails": {
"credentialType": "Basic",
"credentials": "{\"credentialData\":[{\"name\":\"username\",\"value\":\"user\"},{\"name\":\"password\",\"value\":\"pass\"}]}",
"encryptedConnection": "Encrypted",
"encryptionAlgorithm": "None",
"privacyLevel": "Organizational"
}
}'7 - Take Over Ownership
When a semantic model owner leaves the organization:
WS_ID=$(fab get "ws.Workspace" -q "id" | tr -d '"')
MODEL_ID=$(fab get "ws.Workspace/Model.SemanticModel" -q "id" | tr -d '"')
# Take over semantic model ownership
fab api -A powerbi "groups/$WS_ID/datasets/$MODEL_ID/Default.TakeOver" -X post8 - Common Patterns
Dev to Production Deployment
#!/bin/bash
DEV_WS="Dev.Workspace"
PROD_WS="Prod.Workspace"
# 1. Export from dev
fab export "$DEV_WS/Sales.SemanticModel" -o ./deploy -f
fab export "$DEV_WS/SalesReport.Report" -o ./deploy -f
# 2. Import to prod
fab import "$PROD_WS/Sales.SemanticModel" -i ./deploy/Sales.SemanticModel -f
fab import "$PROD_WS/SalesReport.Report" -i ./deploy/SalesReport.Report -f
# 3. Trigger refresh
PROD_WS_ID=$(fab get "$PROD_WS" -q "id" | tr -d '"')
MODEL_ID=$(fab get "$PROD_WS/Sales.SemanticModel" -q "id" | tr -d '"')
fab api -A powerbi "groups/$PROD_WS_ID/datasets/$MODEL_ID/refreshes" -X post -i '{"type":"Full"}'
# 4. Verify
fab api -A powerbi "groups/$PROD_WS_ID/datasets/$MODEL_ID/refreshes?\$top=1" -q "value[0].status"Backup Semantic Model
# Export definition for version control
fab export "Prod.Workspace/Model.SemanticModel" -o ./backups/$(date +%Y%m%d) -f
git add ./backups/
git commit -m "Backup Model $(date +%Y-%m-%d)"Incremental Refresh Setup
For large models, use incremental refresh:
1. Configure in Power BI Desktop with RangeStart/RangeEnd parameters 2. Publish to workspace 3. First refresh creates partitions:
# Monitor partition creation
fab api -A powerbi "groups/$WS_ID/datasets/$MODEL_ID/refreshes?\$top=5"9 - Safety Guidelines
- Always verify workspace context before refresh operations
- Test in dev first — never refresh production without testing
- Monitor refresh duration — set appropriate timeouts
- Backup before major changes — export definition before updates
- Use enhanced refresh for large models to avoid timeouts
10 - References
For detailed patterns, see:
- references/semantic-models.md — Full TMDL operations
- references/reports.md — Report management
- references/refresh.md — Refresh troubleshooting
- references/dax-queries.md — Advanced DAX patterns
- references/gateways.md — Gateway configuration
Fabric CLI Power BI Skill
Skill for working with Power BI items (semantic models, reports, dashboards) using the Fabric CLI.
When to Load
Load this skill when:
- Working with semantic models (datasets) — refresh, DAX, TMDL
- Managing Power BI reports — export, rebind, clone
- Querying data via DAX
- Managing gateways and data sources
- Troubleshooting refresh failures
Prerequisites
- Load
fabric-cli-coreskill first - User authenticated via
fab auth login - Access to workspace containing Power BI items
Contents
| File | Description |
|---|---|
| SKILL.md | Main skill definition |
| references/semantic-models.md | Semantic model operations |
| references/reports.md | Report operations |
| references/refresh.md | Refresh operations and troubleshooting |
| references/dax-queries.md | DAX query execution |
| references/gateways.md | Gateway and data source management |
Scripts
Automation scripts for Power BI tasks:
| Script | Description | Usage |
|---|---|---|
refresh_model.py | Trigger and monitor semantic model refresh | python scripts/refresh_model.py <model> [--wait] |
list_refresh_history.py | Show refresh history and failure details | python scripts/list_refresh_history.py <model> [--last N] |
rebind_report.py | Rebind report to different semantic model | python scripts/rebind_report.py <report> --model <new-model> |
DAX Query Execution
Execute DAX queries against semantic models using the Power BI REST API via Fabric CLI.
Prerequisites
# Get model ID
MODEL_ID=$(fab get "ws.Workspace/Model.SemanticModel" -q "id" | tr -d '"')Basic Queries
Simple EVALUATE
fab api -A powerbi "datasets/$MODEL_ID/executeQueries" -X post -i '{
"queries": [{"query": "EVALUATE VALUES(Date[Year])"}]
}'Table Preview
fab api -A powerbi "datasets/$MODEL_ID/executeQueries" -X post -i '{
"queries": [{"query": "EVALUATE TOPN(100, Sales)"}]
}'Multiple Queries
fab api -A powerbi "datasets/$MODEL_ID/executeQueries" -X post -i '{
"queries": [
{"query": "EVALUATE VALUES(Date[Year])"},
{"query": "EVALUATE VALUES(Product[Category])"}
]
}'Aggregation Queries
SUMMARIZE
fab api -A powerbi "datasets/$MODEL_ID/executeQueries" -X post -i '{
"queries": [{
"query": "EVALUATE SUMMARIZE(Sales, Date[Year], \"Total\", SUM(Sales[Amount]))"
}]
}'SUMMARIZECOLUMNS
fab api -A powerbi "datasets/$MODEL_ID/executeQueries" -X post -i '{
"queries": [{
"query": "EVALUATE SUMMARIZECOLUMNS(Date[Year], Product[Category], \"Revenue\", SUM(Sales[Amount]), \"Qty\", SUM(Sales[Quantity]))"
}]
}'ADDCOLUMNS
fab api -A powerbi "datasets/$MODEL_ID/executeQueries" -X post -i '{
"queries": [{
"query": "EVALUATE ADDCOLUMNS(VALUES(Product[Category]), \"Total\", CALCULATE(SUM(Sales[Amount])))"
}]
}'Filter Queries
FILTER
fab api -A powerbi "datasets/$MODEL_ID/executeQueries" -X post -i '{
"queries": [{
"query": "EVALUATE FILTER(Sales, Sales[Amount] > 1000)"
}]
}'CALCULATETABLE
fab api -A powerbi "datasets/$MODEL_ID/executeQueries" -X post -i '{
"queries": [{
"query": "EVALUATE CALCULATETABLE(VALUES(Product[Name]), Sales[Year] = 2024)"
}]
}'TOPN with Filter
fab api -A powerbi "datasets/$MODEL_ID/executeQueries" -X post -i '{
"queries": [{
"query": "EVALUATE TOPN(10, FILTER(Product, Product[Category] = \"Electronics\"), [Total Sales], DESC)"
}]
}'Parameterized Queries
Single Parameter
fab api -A powerbi "datasets/$MODEL_ID/executeQueries" -X post -i '{
"queries": [{
"query": "EVALUATE FILTER(Sales, Sales[Year] = @Year)",
"parameters": [{"name": "@Year", "value": "2024"}]
}]
}'Multiple Parameters
fab api -A powerbi "datasets/$MODEL_ID/executeQueries" -X post -i '{
"queries": [{
"query": "EVALUATE FILTER(Sales, Sales[Year] >= @StartYear && Sales[Year] <= @EndYear)",
"parameters": [
{"name": "@StartYear", "value": "2022"},
{"name": "@EndYear", "value": "2024"}
]
}]
}'Advanced Patterns
Time Intelligence
# Year-over-Year comparison
fab api -A powerbi "datasets/$MODEL_ID/executeQueries" -X post -i '{
"queries": [{
"query": "EVALUATE ADDCOLUMNS(VALUES(Date[Year]), \"Current\", CALCULATE(SUM(Sales[Amount])), \"Previous\", CALCULATE(SUM(Sales[Amount]), SAMEPERIODLASTYEAR(Date[Date])))"
}]
}'Running Total
fab api -A powerbi "datasets/$MODEL_ID/executeQueries" -X post -i '{
"queries": [{
"query": "EVALUATE ADDCOLUMNS(VALUES(Date[Month]), \"RunningTotal\", CALCULATE(SUM(Sales[Amount]), FILTER(ALL(Date[Month]), Date[Month] <= EARLIER(Date[Month]))))"
}]
}'Distinct Count
fab api -A powerbi "datasets/$MODEL_ID/executeQueries" -X post -i '{
"queries": [{
"query": "EVALUATE ROW(\"UniqueCustomers\", DISTINCTCOUNT(Sales[CustomerID]))"
}]
}'Query Options
Include Nulls
fab api -A powerbi "datasets/$MODEL_ID/executeQueries" -X post -i '{
"queries": [{"query": "EVALUATE VALUES(Customer[Region])"}],
"serializerSettings": {"includeNulls": true}
}'Impersonation (RLS Testing)
fab api -A powerbi "datasets/$MODEL_ID/executeQueries" -X post -i '{
"queries": [{"query": "EVALUATE Sales"}],
"impersonatedUserName": "user@contoso.com"
}'Output Processing
Extract to CSV
fab api -A powerbi "datasets/$MODEL_ID/executeQueries" -X post -i '{
"queries": [{"query": "EVALUATE SUMMARIZECOLUMNS(Date[Year], \"Total\", SUM(Sales[Amount]))"}]
}' | jq -r '.results[0].tables[0].rows[] | [.[]] | @csv'Extract to JSON file
fab api -A powerbi "datasets/$MODEL_ID/executeQueries" -X post -i '{
"queries": [{"query": "EVALUATE Sales"}]
}' -o /tmp/query-results.jsonLimitations
- Maximum query timeout varies by capacity
- Large result sets may be truncated
- Complex queries may hit memory limits
- Some DAX functions not available via REST API
Gateway Operations
Manage on-premises data gateways and data sources using Fabric CLI.
List Gateways
Gateways are tenant-level hidden entities.
# List all gateways (requires admin or gateway permissions)
fab ls .gateways
# List with details
fab ls .gateways -vGet Gateway Info
# Get gateway by name
fab get ".gateways/MyGateway.Gateway"
# Get gateway ID
GATEWAY_ID=$(fab get ".gateways/MyGateway.Gateway" -q "id" | tr -d '"')
# Get gateway via API
fab api -A powerbi "gateways/$GATEWAY_ID"Gateway Cluster Status
# Check gateway cluster members
fab api -A powerbi "gateways/$GATEWAY_ID" -q "gatewayAnnotation"
# Parse status
fab api -A powerbi "gateways/$GATEWAY_ID" | jq '.gatewayStatus'Data Sources
List Data Sources on Gateway
fab api -A powerbi "gateways/$GATEWAY_ID/datasources"Get Data Source Details
DATASOURCE_ID="<datasource-id>"
fab api -A powerbi "gateways/$GATEWAY_ID/datasources/$DATASOURCE_ID"Create Data Source
# SQL Server example
fab api -A powerbi "gateways/$GATEWAY_ID/datasources" -X post -i '{
"dataSourceType": "Sql",
"connectionDetails": "{\"server\":\"myserver.database.windows.net\",\"database\":\"mydb\"}",
"datasourceName": "MyDatabase",
"credentialDetails": {
"credentialType": "Basic",
"credentials": "{\"credentialData\":[{\"name\":\"username\",\"value\":\"user\"},{\"name\":\"password\",\"value\":\"pass\"}]}",
"encryptedConnection": "Encrypted",
"encryptionAlgorithm": "None",
"privacyLevel": "Organizational"
}
}'Update Data Source Credentials
# Update with basic auth
fab api -A powerbi "gateways/$GATEWAY_ID/datasources/$DATASOURCE_ID" -X patch -i '{
"credentialDetails": {
"credentialType": "Basic",
"credentials": "{\"credentialData\":[{\"name\":\"username\",\"value\":\"newuser\"},{\"name\":\"password\",\"value\":\"newpass\"}]}",
"encryptedConnection": "Encrypted",
"encryptionAlgorithm": "None",
"privacyLevel": "Organizational"
}
}'
# Update with OAuth2
fab api -A powerbi "gateways/$GATEWAY_ID/datasources/$DATASOURCE_ID" -X patch -i '{
"credentialDetails": {
"credentialType": "OAuth2",
"credentials": "{\"credentialData\":[{\"name\":\"accessToken\",\"value\":\"<token>\"}]}",
"encryptedConnection": "Encrypted",
"encryptionAlgorithm": "None",
"privacyLevel": "Organizational"
}
}'Delete Data Source
fab api -A powerbi "gateways/$GATEWAY_ID/datasources/$DATASOURCE_ID" -X deleteSemantic Model Data Sources
Get Bound Data Sources
MODEL_ID=$(fab get "ws.Workspace/Model.SemanticModel" -q "id" | tr -d '"')
# Get data sources bound to model
fab api -A powerbi "datasets/$MODEL_ID/Default.GetBoundGatewayDatasources"Bind to Gateway
WS_ID=$(fab get "ws.Workspace" -q "id" | tr -d '"')
fab api -A powerbi "groups/$WS_ID/datasets/$MODEL_ID/Default.BindToGateway" -X post -i '{
"gatewayObjectId": "<gateway-id>",
"datasourceObjectIds": ["<datasource-id-1>", "<datasource-id-2>"]
}'Take Over Data Source
When original owner leaves:
fab api -A powerbi "groups/$WS_ID/datasets/$MODEL_ID/Default.TakeOver" -X postGateway Users
List Gateway Users
fab api -A powerbi "gateways/$GATEWAY_ID/users"Add Gateway User
fab api -A powerbi "gateways/$GATEWAY_ID/users" -X post -i '{
"emailAddress": "user@contoso.com",
"datasourceAccessRight": "Read"
}'Remove Gateway User
fab api -A powerbi "gateways/$GATEWAY_ID/users/$USER_ID" -X deleteTroubleshooting
Gateway Not Reachable
1. Check gateway service is running on gateway machine 2. Verify network connectivity 3. Check firewall rules 4. Review gateway logs
Data Source Connection Failed
1. Test connection string manually 2. Verify credentials are current 3. Check if source database is accessible from gateway machine 4. Review gateway data source configuration
Gateway Commands Summary
| Operation | Command |
|---|---|
| List gateways | fab ls .gateways |
| Get gateway | fab api -A powerbi "gateways/$GW_ID" |
| List data sources | fab api -A powerbi "gateways/$GW_ID/datasources" |
| Update credentials | fab api -A powerbi "gateways/$GW_ID/datasources/$DS_ID" -X patch -i '...' |
| Bind model to gateway | fab api -A powerbi "groups/$WS_ID/datasets/$MODEL_ID/Default.BindToGateway" -X post -i '...' |
Refresh Operations
Comprehensive guide for managing semantic model refresh operations using Fabric CLI.
Basic Refresh
Get Required IDs
# Workspace ID
WS_ID=$(fab get "ws.Workspace" -q "id" | tr -d '"')
# Model ID
MODEL_ID=$(fab get "ws.Workspace/Model.SemanticModel" -q "id" | tr -d '"')Trigger Full Refresh
fab api -A powerbi "groups/$WS_ID/datasets/$MODEL_ID/refreshes" -X post -i '{"type":"Full"}'Check Refresh Status
# Latest refresh
fab api -A powerbi "groups/$WS_ID/datasets/$MODEL_ID/refreshes?\$top=1"
# Refresh history
fab api -A powerbi "groups/$WS_ID/datasets/$MODEL_ID/refreshes"Enhanced Refresh
Enhanced refresh provides more control for large models.
Table-Level Refresh
fab api -A powerbi "groups/$WS_ID/datasets/$MODEL_ID/refreshes" -X post -i '{
"type": "Full",
"commitMode": "transactional",
"objects": [
{"table": "Sales"},
{"table": "Products"}
]
}'Partition-Level Refresh
fab api -A powerbi "groups/$WS_ID/datasets/$MODEL_ID/refreshes" -X post -i '{
"type": "Full",
"commitMode": "transactional",
"objects": [
{"table": "Sales", "partition": "Sales-2024-Q1"},
{"table": "Sales", "partition": "Sales-2024-Q2"}
]
}'Refresh with Retry
fab api -A powerbi "groups/$WS_ID/datasets/$MODEL_ID/refreshes" -X post -i '{
"type": "Full",
"retryCount": 3,
"maxParallelism": 4
}'Apply Incremental Refresh Policy
fab api -A powerbi "groups/$WS_ID/datasets/$MODEL_ID/refreshes" -X post -i '{
"type": "Full",
"applyRefreshPolicy": true
}'Refresh Scheduling
Get Current Schedule
fab api -A powerbi "datasets/$MODEL_ID/refreshSchedule"Set Daily Schedule
fab api -A powerbi "datasets/$MODEL_ID/refreshSchedule" -X patch -i '{
"enabled": true,
"days": ["Monday","Tuesday","Wednesday","Thursday","Friday"],
"times": ["06:00", "18:00"],
"localTimeZoneId": "UTC"
}'Set Weekly Schedule
fab api -A powerbi "datasets/$MODEL_ID/refreshSchedule" -X patch -i '{
"enabled": true,
"days": ["Sunday"],
"times": ["02:00"],
"localTimeZoneId": "Pacific Standard Time"
}'Disable Schedule
fab api -A powerbi "datasets/$MODEL_ID/refreshSchedule" -X patch -i '{
"enabled": false
}'Troubleshooting Refresh Failures
Common Error Patterns
| Error | Cause | Solution |
|---|---|---|
InvalidCredentials | Data source credentials expired | Update credentials in gateway settings |
GatewayNotReachable | Gateway offline | Check gateway server, restart service |
DataSourceNotFound | Connection string changed | Update data source configuration |
OutOfMemory | Model too large | Use incremental refresh, optimize model |
Timeout | Refresh exceeded limit | Use enhanced refresh with smaller batches |
QueryTimeout | Source query too slow | Optimize source queries, add indexes |
Get Detailed Error Info
# Get failed refresh details
fab api -A powerbi "groups/$WS_ID/datasets/$MODEL_ID/refreshes?\$top=10" | \
jq '.value[] | select(.status == "Failed") | {startTime, endTime, status, serviceExceptionJson}'Check Data Source Status
# List bound gateways
fab api -A powerbi "datasets/$MODEL_ID/Default.GetBoundGatewayDatasources"
# Check gateway status
GATEWAY_ID="<gateway-id>"
fab api -A powerbi "gateways/$GATEWAY_ID"Cancel Running Refresh
REFRESH_ID="<refresh-request-id>"
fab api -A powerbi "groups/$WS_ID/datasets/$MODEL_ID/refreshes/$REFRESH_ID" -X deleteMonitoring Patterns
Script: Monitor Refresh Until Complete
#!/bin/bash
WS_ID=$1
MODEL_ID=$2
# Trigger refresh
fab api -A powerbi "groups/$WS_ID/datasets/$MODEL_ID/refreshes" -X post -i '{"type":"Full"}'
# Poll until complete
while true; do
STATUS=$(fab api -A powerbi "groups/$WS_ID/datasets/$MODEL_ID/refreshes?\$top=1" -q "value[0].status")
echo "Status: $STATUS"
case $STATUS in
"Completed") echo "Refresh successful"; exit 0 ;;
"Failed") echo "Refresh failed"; exit 1 ;;
"Cancelled") echo "Refresh cancelled"; exit 2 ;;
*) sleep 30 ;;
esac
doneScript: Refresh with Notification
#!/bin/bash
WS_ID=$1
MODEL_ID=$2
EMAIL=$3
fab api -A powerbi "groups/$WS_ID/datasets/$MODEL_ID/refreshes" -X post -i '{"type":"Full"}'
# Wait and check
sleep 300 # Wait 5 minutes
STATUS=$(fab api -A powerbi "groups/$WS_ID/datasets/$MODEL_ID/refreshes?\$top=1" -q "value[0].status")
if [ "$STATUS" != "Completed" ]; then
echo "Refresh status: $STATUS - sending alert"
# Add notification logic here
fiIncremental Refresh
For large historical tables, use incremental refresh.
Prerequisites
1. Model must have RangeStart and RangeEnd date parameters 2. Source query must filter on date column using these parameters 3. Configure refresh policy in Power BI Desktop before publishing
How It Works
- Historical partitions: Loaded once, not refreshed
- Rolling window: Configurable period always refreshed
- Current partition: Contains most recent data
Monitor Partition Creation
# After first refresh, check partitions were created
fab api -A powerbi "groups/$WS_ID/datasets/$MODEL_ID/refreshes?\$top=1" -q "value[0]"Override Incremental Policy (XMLA)
For advanced scenarios, use XMLA endpoint to:
- Refresh specific historical partitions
- Bootstrap large initial loads
- Bypass refresh policy for one-time operations
Reports Reference
Working with Power BI reports via Fabric CLI.
Item Type
Use .Report suffix when referencing reports:
# Reference format
ws.Workspace/ReportName.ReportList Reports
# List in current workspace
fab ls . -t Report
# List in specific workspace
fab ls "ws.Workspace" -t Report
# List with details
fab ls "ws.Workspace" -t Report -vGet Report Details
# Get report metadata
fab get "ws.Workspace/SalesReport.Report"
# Get specific property
fab get "ws.Workspace/SalesReport.Report" -q "id"
# Get report via API
WS_ID=$(fab get "ws.Workspace" -q "id" | tr -d '"')
REPORT_ID=$(fab get "ws.Workspace/SalesReport.Report" -q "id" | tr -d '"')
fab api -A powerbi "groups/$WS_ID/reports/$REPORT_ID"Export Reports
Export as PBIX
fab export "ws.Workspace/SalesReport.Report" -d ./exportsExport to File Format
# Export to PDF
fab api -A powerbi "groups/$WS_ID/reports/$REPORT_ID/ExportTo" -X post -i '{
"format": "PDF"
}' -o report.pdf
# Export to PNG
fab api -A powerbi "groups/$WS_ID/reports/$REPORT_ID/ExportTo" -X post -i '{
"format": "PNG",
"powerBIReportConfiguration": {
"pages": [{"pageName": "Page1"}]
}
}' -o report.png
# Export to PPTX (PowerPoint)
fab api -A powerbi "groups/$WS_ID/reports/$REPORT_ID/ExportTo" -X post -i '{
"format": "PPTX"
}' -o report.pptxExport Paginated Report
# Export to PDF with parameters
fab api -A powerbi "groups/$WS_ID/reports/$REPORT_ID/ExportTo" -X post -i '{
"format": "PDF",
"paginatedReportConfiguration": {
"parameterValues": [
{"name": "Year", "value": "2024"},
{"name": "Region", "value": "West"}
]
}
}'Async Export (Large Reports)
# Start export job
EXPORT_RESULT=$(fab api -A powerbi "groups/$WS_ID/reports/$REPORT_ID/ExportTo" -X post -i '{
"format": "PDF"
}')
EXPORT_ID=$(echo $EXPORT_RESULT | jq -r '.id')
# Poll for completion
fab api -A powerbi "groups/$WS_ID/reports/$REPORT_ID/exports/$EXPORT_ID"
# Download when complete
fab api -A powerbi "groups/$WS_ID/reports/$REPORT_ID/exports/$EXPORT_ID/file" -o report.pdfImport Reports
Import PBIX (Contains Report)
fab import ./report.pbix -d "ws.Workspace"
# Skip creating new dataset (rebind to existing)
fab import ./report.pbix -d "ws.Workspace" -c CreateOrOverwriteClone Report
# Clone within same workspace
fab api -A powerbi "groups/$WS_ID/reports/$REPORT_ID/Clone" -X post -i '{
"name": "SalesReport_Copy"
}'
# Clone to different workspace
TARGET_WS_ID=$(fab get "target.Workspace" -q "id" | tr -d '"')
fab api -A powerbi "groups/$WS_ID/reports/$REPORT_ID/Clone" -X post -i '{
"name": "SalesReport_Copy",
"targetWorkspaceId": "'"$TARGET_WS_ID"'"
}'
# Clone and rebind to different dataset
TARGET_MODEL_ID=$(fab get "target.Workspace/Model.SemanticModel" -q "id" | tr -d '"')
fab api -A powerbi "groups/$WS_ID/reports/$REPORT_ID/Clone" -X post -i '{
"name": "SalesReport_Copy",
"targetWorkspaceId": "'"$TARGET_WS_ID"'",
"targetModelId": "'"$TARGET_MODEL_ID"'"
}'Rebind Report
Change the semantic model a report connects to:
# Get current binding
fab api -A powerbi "groups/$WS_ID/reports/$REPORT_ID"
# Rebind to new dataset in same workspace
NEW_MODEL_ID=$(fab get "ws.Workspace/NewModel.SemanticModel" -q "id" | tr -d '"')
fab api -A powerbi "groups/$WS_ID/reports/$REPORT_ID/Rebind" -X post -i '{
"datasetId": "'"$NEW_MODEL_ID"'"
}'Report Pages
Get Pages
fab api -A powerbi "groups/$WS_ID/reports/$REPORT_ID/pages"Get Single Page
fab api -A powerbi "groups/$WS_ID/reports/$REPORT_ID/pages/Page1"Update Report
Update Report Content
fab api -A powerbi "groups/$WS_ID/reports/$REPORT_ID/UpdateReportContent" -X post -i '{
"sourceReport": {
"sourceReportId": "'"$SOURCE_REPORT_ID"'",
"sourceWorkspaceId": "'"$SOURCE_WS_ID"'"
},
"sourceType": "ExistingReport"
}'Delete Report
# Delete via fab
fab rm "ws.Workspace/SalesReport.Report"
# Delete via API
fab api -A powerbi "groups/$WS_ID/reports/$REPORT_ID" -X deleteReport Permissions
Get Report Users
fab api -A powerbi "groups/$WS_ID/reports/$REPORT_ID/users"Subscriptions
Get Report Subscriptions
fab api -A powerbi "groups/$WS_ID/reports/$REPORT_ID/subscriptions"Quick Reference
| Operation | Command |
|---|---|
| List reports | fab ls "ws.Workspace" -t Report |
| Get report | fab get "ws.Workspace/Report.Report" |
| Export PBIX | fab export "ws.Workspace/Report.Report" -d ./ |
| Export PDF | fab api -A powerbi "groups/$WS_ID/reports/$REPORT_ID/ExportTo" -X post -i '{"format":"PDF"}' |
| Clone report | fab api -A powerbi "groups/$WS_ID/reports/$REPORT_ID/Clone" -X post -i '{...}' |
| Rebind dataset | fab api -A powerbi "groups/$WS_ID/reports/$REPORT_ID/Rebind" -X post -i '{...}' |
| Delete report | fab rm "ws.Workspace/Report.Report" |
See Also
- Semantic Models - Manage underlying data
- Refresh Operations - Refresh data
Semantic Models Reference
Working with Power BI semantic models (formerly datasets) via Fabric CLI.
Item Type
Use .SemanticModel suffix when referencing semantic models:
# Reference format
ws.Workspace/ModelName.SemanticModelList Semantic Models
# List in current workspace
fab ls . -t SemanticModel
# List in specific workspace
fab ls "ws.Workspace" -t SemanticModel
# List with verbose details
fab ls "ws.Workspace" -t SemanticModel -vGet Model Details
# Get model metadata
fab get "ws.Workspace/Model.SemanticModel"
# Get specific property
fab get "ws.Workspace/Model.SemanticModel" -q "id"
# Store ID for API calls
MODEL_ID=$(fab get "ws.Workspace/Model.SemanticModel" -q "id" | tr -d '"')Export Semantic Model
Export as PBIX (Power BI Desktop)
fab export "ws.Workspace/Model.SemanticModel" -d ./exportsExport via API
WS_ID=$(fab get "ws.Workspace" -q "id" | tr -d '"')
MODEL_ID=$(fab get "ws.Workspace/Model.SemanticModel" -q "id" | tr -d '"')
# Start export
fab api -A powerbi "groups/$WS_ID/datasets/$MODEL_ID/Default.Export" -X post -o model.pbixImport Semantic Model
Import PBIX
fab import ./model.pbix -d "ws.Workspace"
# With conflict handling (CreateOrOverwrite)
fab import ./model.pbix -d "ws.Workspace" -c CreateOrOverwriteImport via Large Upload API
For files >1GB:
# Create upload session
fab api -A powerbi "groups/$WS_ID/imports/createUploadSession" -X post -i '{
"nameConflict": "CreateOrOverwrite",
"name": "LargeModel"
}'Clone Semantic Model
# Get source model details
SOURCE_WS_ID=$(fab get "source.Workspace" -q "id" | tr -d '"')
SOURCE_MODEL_ID=$(fab get "source.Workspace/Model.SemanticModel" -q "id" | tr -d '"')
TARGET_WS_ID=$(fab get "target.Workspace" -q "id" | tr -d '"')
# Clone to target workspace
fab api -A powerbi "groups/$SOURCE_WS_ID/datasets/$SOURCE_MODEL_ID/Default.Clone" -X post -i '{
"targetWorkspaceId": "'"$TARGET_WS_ID"'",
"targetModelName": "ModelCopy"
}'Model Configuration
Get Model Parameters
fab api -A powerbi "groups/$WS_ID/datasets/$MODEL_ID/parameters"Update Parameters
fab api -A powerbi "groups/$WS_ID/datasets/$MODEL_ID/Default.UpdateParameters" -X post -i '{
"updateDetails": [
{
"name": "ServerName",
"newValue": "newserver.database.windows.net"
},
{
"name": "DatabaseName",
"newValue": "newdatabase"
}
]
}'Get Data Sources
fab api -A powerbi "groups/$WS_ID/datasets/$MODEL_ID/datasources"Update Data Sources
fab api -A powerbi "groups/$WS_ID/datasets/$MODEL_ID/Default.UpdateDatasources" -X post -i '{
"updateDetails": [
{
"datasourceSelector": {
"datasourceType": "Sql",
"connectionDetails": {
"server": "oldserver.database.windows.net",
"database": "olddb"
}
},
"connectionDetails": {
"server": "newserver.database.windows.net",
"database": "newdb"
}
}
]
}'Model Permissions
Take Over Ownership
fab api -A powerbi "groups/$WS_ID/datasets/$MODEL_ID/Default.TakeOver" -X postGet Model Users
fab api -A powerbi "groups/$WS_ID/datasets/$MODEL_ID/users"Add Model User
fab api -A powerbi "groups/$WS_ID/datasets/$MODEL_ID/users" -X post -i '{
"identifier": "user@contoso.com",
"principalType": "User",
"datasetUserAccessRight": "Read"
}'Model Lineage
Get Upstream Datasets
fab api -A powerbi "groups/$WS_ID/datasets/$MODEL_ID/upstreamDatasets"Quick Reference
| Operation | Command |
|---|---|
| List models | fab ls "ws.Workspace" -t SemanticModel |
| Get model | fab get "ws.Workspace/Model.SemanticModel" |
| Export PBIX | fab export "ws.Workspace/Model.SemanticModel" -d ./ |
| Import PBIX | fab import ./model.pbix -d "ws.Workspace" |
| Get parameters | fab api -A powerbi "groups/$WS_ID/datasets/$MODEL_ID/parameters" |
| Take over | fab api -A powerbi "groups/$WS_ID/datasets/$MODEL_ID/Default.TakeOver" -X post |
See Also
- Refresh Operations - Refresh semantic models
- DAX Queries - Query semantic model data
- Gateways - Gateway binding
#!/usr/bin/env python3
"""
list_refresh_history.py - Show refresh history and failure details
This script displays the refresh history for a semantic model, including
duration, status, and error details.
Usage:
python list_refresh_history.py <model> [--last N]
python list_refresh_history.py Production.Workspace/Sales.SemanticModel --last 10
Exit codes:
0 - History retrieved successfully
1 - Failed to retrieve history
"""
import argparse
import json
import subprocess
import sys
from datetime import datetime, timedelta
from typing import Dict, Any, List, Optional
def run_fab_command(args: list[str], timeout: int = 60) -> tuple[int, str, str]:
"""Execute a fab CLI command and return exit code, stdout, stderr."""
try:
result = subprocess.run(
["fab"] + args,
capture_output=True,
text=True,
timeout=timeout
)
return result.returncode, result.stdout, result.stderr
except FileNotFoundError:
return -1, "", "fab CLI not found in PATH"
except subprocess.TimeoutExpired:
return -2, "", f"Command timed out after {timeout} seconds"
except Exception as e:
return -3, "", str(e)
def ensure_semantic_model_suffix(path: str) -> str:
"""Ensure path has .SemanticModel suffix if it's just a model path."""
# If it already ends with a type suffix, return as is
known_suffixes = [".SemanticModel", ".Dataset", ".Workspace"]
for suffix in known_suffixes:
if path.endswith(suffix):
return path
# If path contains a workspace but no model type, add SemanticModel
if "/" in path and not path.split("/")[-1].count("."):
return f"{path}.SemanticModel"
return path
def check_path_exists(path: str) -> bool:
"""Check if path exists."""
exit_code, _, _ = run_fab_command(["exists", path])
return exit_code == 0
def get_refresh_history(model_path: str, limit: int = 10) -> List[Dict[str, Any]]:
"""Get refresh history for a semantic model."""
# Use the jobs list command to get refresh history
cmd = ["jobs", "list", model_path, "-t", "Refresh", "-l", str(limit), "-f", "json"]
exit_code, stdout, stderr = run_fab_command(cmd)
if exit_code == 0:
try:
result = json.loads(stdout.strip())
if isinstance(result, list):
return result
except json.JSONDecodeError:
pass
return []
def get_job_details(job_id: str, model_path: str) -> Dict[str, Any]:
"""Get detailed information about a specific job."""
cmd = ["jobs", "get", model_path, "-j", job_id, "-f", "json"]
exit_code, stdout, stderr = run_fab_command(cmd)
if exit_code == 0:
try:
return json.loads(stdout.strip())
except json.JSONDecodeError:
pass
return {}
def format_duration(start_time: str, end_time: str) -> str:
"""Format duration between two ISO timestamps."""
try:
start = datetime.fromisoformat(start_time.replace("Z", "+00:00"))
end = datetime.fromisoformat(end_time.replace("Z", "+00:00"))
duration = end - start
total_seconds = int(duration.total_seconds())
hours, remainder = divmod(total_seconds, 3600)
minutes, seconds = divmod(remainder, 60)
if hours > 0:
return f"{hours}h {minutes}m {seconds}s"
elif minutes > 0:
return f"{minutes}m {seconds}s"
else:
return f"{seconds}s"
except:
return "N/A"
def format_timestamp(timestamp: str) -> str:
"""Format ISO timestamp for display."""
try:
dt = datetime.fromisoformat(timestamp.replace("Z", "+00:00"))
return dt.strftime("%Y-%m-%d %H:%M:%S UTC")
except:
return timestamp
def analyze_failure_patterns(history: List[Dict[str, Any]]) -> Dict[str, Any]:
"""Analyze refresh history for failure patterns."""
analysis = {
"total_refreshes": len(history),
"successful": 0,
"failed": 0,
"cancelled": 0,
"in_progress": 0,
"success_rate": 0.0,
"avg_duration_seconds": 0,
"common_errors": {},
"recent_failures": []
}
durations = []
for refresh in history:
status = refresh.get("status", "").lower()
if status in ["completed", "succeeded", "success"]:
analysis["successful"] += 1
# Calculate duration for successful refreshes
start = refresh.get("startTime", refresh.get("startDateTime"))
end = refresh.get("endTime", refresh.get("endDateTime"))
if start and end:
try:
start_dt = datetime.fromisoformat(start.replace("Z", "+00:00"))
end_dt = datetime.fromisoformat(end.replace("Z", "+00:00"))
durations.append((end_dt - start_dt).total_seconds())
except:
pass
elif status in ["failed", "error"]:
analysis["failed"] += 1
# Track error messages
error_msg = refresh.get("error", refresh.get("errorMessage", "Unknown error"))
if error_msg:
analysis["common_errors"][error_msg] = analysis["common_errors"].get(error_msg, 0) + 1
# Track recent failures
if len(analysis["recent_failures"]) < 5:
analysis["recent_failures"].append({
"time": refresh.get("startTime", refresh.get("startDateTime", "Unknown")),
"error": error_msg
})
elif status in ["cancelled", "canceled"]:
analysis["cancelled"] += 1
elif status in ["running", "in_progress", "inprogress"]:
analysis["in_progress"] += 1
# Calculate success rate
completed = analysis["successful"] + analysis["failed"]
if completed > 0:
analysis["success_rate"] = round(analysis["successful"] / completed * 100, 1)
# Calculate average duration
if durations:
analysis["avg_duration_seconds"] = round(sum(durations) / len(durations), 1)
return analysis
def print_refresh_table(history: List[Dict[str, Any]]):
"""Print refresh history in a formatted table."""
if not history:
print("No refresh history found.")
return
# Table header
print("\n{:<6} {:<22} {:<12} {:<12} {}".format(
"#", "Started", "Duration", "Status", "Details"
))
print("-" * 80)
for i, refresh in enumerate(history, 1):
# Extract fields
start_time = refresh.get("startTime", refresh.get("startDateTime", "N/A"))
end_time = refresh.get("endTime", refresh.get("endDateTime", ""))
status = refresh.get("status", "Unknown")
error = refresh.get("error", refresh.get("errorMessage", ""))
# Format values
start_formatted = format_timestamp(start_time)[:22] if start_time != "N/A" else "N/A"
duration = format_duration(start_time, end_time) if end_time else "Running"
# Status emoji
status_display = status
if status.lower() in ["completed", "succeeded", "success"]:
status_display = "✓ Success"
elif status.lower() in ["failed", "error"]:
status_display = "✗ Failed"
elif status.lower() in ["running", "in_progress"]:
status_display = "⟳ Running"
elif status.lower() in ["cancelled", "canceled"]:
status_display = "⊘ Cancelled"
# Details (truncate if too long)
details = error[:30] + "..." if len(error) > 30 else error
print("{:<6} {:<22} {:<12} {:<12} {}".format(
i, start_formatted, duration, status_display, details
))
def list_refresh_history(
model_path: str,
limit: int = 10,
show_details: bool = False
) -> Dict[str, Any]:
"""List refresh history for a semantic model."""
model_path = ensure_semantic_model_suffix(model_path)
report = {
"model": model_path,
"limit": limit,
"history": [],
"analysis": {}
}
# Verify model exists
print(f"\nVerifying model: {model_path}")
if not check_path_exists(model_path):
print(f"Error: Model '{model_path}' does not exist or is not accessible")
return report
print(" Model found")
# Get refresh history
print(f"\nFetching refresh history (last {limit})...")
history = get_refresh_history(model_path, limit)
report["history"] = history
if not history:
print(" No refresh history found")
return report
print(f" Found {len(history)} refresh record(s)")
# Get additional details if requested
if show_details:
print("\nFetching detailed information...")
for i, refresh in enumerate(history):
job_id = refresh.get("id", refresh.get("jobId"))
if job_id:
details = get_job_details(job_id, model_path)
if details:
history[i].update(details)
# Print table
print_refresh_table(history)
# Analyze patterns
print("\nAnalyzing refresh patterns...")
analysis = analyze_failure_patterns(history)
report["analysis"] = analysis
return report
def main():
parser = argparse.ArgumentParser(
description="Show refresh history and failure details for a semantic model",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Examples:
python list_refresh_history.py Production.Workspace/Sales.SemanticModel
python list_refresh_history.py Production.Workspace/Sales.SemanticModel --last 20
python list_refresh_history.py "My Workspace/Revenue Model" --last 5 --details
Output includes:
- Refresh start time
- Duration
- Status (Success, Failed, Running, Cancelled)
- Error details for failed refreshes
- Success rate analysis
- Common error patterns
"""
)
parser.add_argument(
"model",
help="Semantic model path (Workspace/Model.SemanticModel)"
)
parser.add_argument(
"--last", "-n",
type=int,
default=10,
help="Number of recent refreshes to show (default: 10)"
)
parser.add_argument(
"--details", "-d",
action="store_true",
help="Fetch detailed information for each refresh"
)
parser.add_argument(
"--json", "-j",
action="store_true",
help="Output results in JSON format"
)
args = parser.parse_args()
try:
report = list_refresh_history(
model_path=args.model,
limit=args.last,
show_details=args.details
)
# Print analysis summary
if report["analysis"]:
analysis = report["analysis"]
print("\n" + "=" * 50)
print("REFRESH ANALYSIS")
print("=" * 50)
print(f"Total Refreshes: {analysis['total_refreshes']}")
print(f" Successful: {analysis['successful']}")
print(f" Failed: {analysis['failed']}")
print(f" Cancelled: {analysis['cancelled']}")
print(f" In Progress: {analysis['in_progress']}")
print(f"\nSuccess Rate: {analysis['success_rate']}%")
if analysis['avg_duration_seconds'] > 0:
avg_mins = analysis['avg_duration_seconds'] / 60
print(f"Avg Duration: {avg_mins:.1f} minutes")
if analysis['common_errors']:
print("\nCommon Errors:")
for error, count in sorted(analysis['common_errors'].items(), key=lambda x: -x[1])[:3]:
print(f" [{count}x] {error[:60]}...")
print("=" * 50)
if args.json:
print("\n" + json.dumps(report, indent=2, default=str))
# Exit code: 0 if we got history, 1 if not
sys.exit(0 if report['history'] else 1)
except Exception as e:
print(f"Error: {e}", file=sys.stderr)
sys.exit(1)
if __name__ == "__main__":
main()
#!/usr/bin/env python3
"""
rebind_report.py - Rebind report to different semantic model
This script changes the semantic model connection for a Power BI report in
Microsoft Fabric.
Usage:
python rebind_report.py <report> --model <new-model>
python rebind_report.py Production.Workspace/SalesReport.Report --model Production.Workspace/NewSales.SemanticModel
Exit codes:
0 - Report successfully rebound to new model
1 - Rebind failed or error occurred
"""
import argparse
import json
import subprocess
import sys
import time
from typing import Dict, Any, Optional, Tuple
def run_fab_command(args: list[str], timeout: int = 60) -> tuple[int, str, str]:
"""Execute a fab CLI command and return exit code, stdout, stderr."""
try:
result = subprocess.run(
["fab"] + args,
capture_output=True,
text=True,
timeout=timeout
)
return result.returncode, result.stdout, result.stderr
except FileNotFoundError:
return -1, "", "fab CLI not found in PATH"
except subprocess.TimeoutExpired:
return -2, "", f"Command timed out after {timeout} seconds"
except Exception as e:
return -3, "", str(e)
def parse_json_output(output: str) -> Any:
"""Parse JSON output from fab CLI."""
try:
return json.loads(output.strip())
except json.JSONDecodeError:
return None
def parse_item_path(path: str, item_type: str) -> Tuple[Optional[str], Optional[str]]:
"""Parse workspace and item name from path."""
parts = path.split("/")
if len(parts) != 2:
return None, None
workspace = parts[0]
item = parts[1]
# Ensure workspace suffix
if not workspace.endswith(".Workspace"):
workspace = f"{workspace}.Workspace"
# Ensure item suffix
suffix = f".{item_type}"
if not item.endswith(suffix):
item = f"{item}{suffix}"
return workspace, item
def check_path_exists(path: str) -> bool:
"""Check if path exists."""
exit_code, _, _ = run_fab_command(["exists", path])
return exit_code == 0
def get_item_details(path: str) -> Dict[str, Any]:
"""Get item details."""
exit_code, stdout, stderr = run_fab_command(["get", path, "-f", "json"])
if exit_code == 0:
return parse_json_output(stdout) or {"error": "Failed to parse item details"}
return {"error": stderr.strip() or "Failed to get item details"}
def get_item_id(path: str) -> Optional[str]:
"""Get item ID from path."""
exit_code, stdout, stderr = run_fab_command(["get", path, "-q", "id", "-f", "json"])
if exit_code == 0:
result = parse_json_output(stdout)
if isinstance(result, str):
return result.strip('"')
return result
return None
def get_report_datasource(report_path: str) -> Dict[str, Any]:
"""Get the current datasource configuration for a report."""
exit_code, stdout, stderr = run_fab_command(["get", report_path, "-f", "json"])
if exit_code == 0:
details = parse_json_output(stdout) or {}
return {
"current_model": details.get("semanticModelId") or details.get("datasetId"),
"workspace_id": details.get("workspaceId"),
"details": details
}
return {"error": stderr}
def rebind_report(workspace_id: str, report_id: str, new_model_id: str,
new_model_workspace_id: Optional[str] = None) -> Dict[str, Any]:
"""Rebind report to a new semantic model using the API."""
result = {
"status": "unknown",
"message": "",
"report_id": report_id,
"new_model_id": new_model_id
}
# Build the API request body
request_body = {
"datasetId": new_model_id
}
# If model is in different workspace, include workspace ID
if new_model_workspace_id and new_model_workspace_id != workspace_id:
request_body["datasetWorkspaceId"] = new_model_workspace_id
# Use the API command to rebind
# POST /groups/{workspaceId}/reports/{reportId}/Rebind
api_path = f"/groups/{workspace_id}/reports/{report_id}/Rebind"
exit_code, stdout, stderr = run_fab_command([
"api", "post", api_path,
"-b", json.dumps(request_body),
"--api-version", "v1.0",
"-f", "json"
], timeout=60)
if exit_code == 0:
result["status"] = "success"
result["message"] = "Report successfully rebound to new semantic model"
else:
# Check for specific error conditions
if "already bound" in stderr.lower() or "same dataset" in stderr.lower():
result["status"] = "success"
result["message"] = "Report is already bound to the specified model"
else:
result["status"] = "failed"
result["message"] = stderr.strip() or "Failed to rebind report"
return result
def verify_rebind(report_path: str, expected_model_id: str) -> bool:
"""Verify that rebind was successful."""
time.sleep(2) # Give it a moment to propagate
datasource = get_report_datasource(report_path)
current_model = datasource.get("current_model")
return current_model == expected_model_id
def print_result(result: Dict[str, Any], output_format: str, output_file: Optional[str] = None):
"""Print rebind result."""
if output_format == "json":
output = json.dumps(result, indent=2)
else:
# Text format
lines = []
lines.append("=" * 60)
lines.append("REPORT REBIND RESULT")
lines.append("=" * 60)
lines.append(f"Report: {result.get('report_path', 'Unknown')}")
lines.append(f"Target Model: {result.get('model_path', 'Unknown')}")
lines.append("")
status = result.get("status", "unknown")
if status == "success":
lines.append("✓ REBIND SUCCESSFUL")
lines.append(f" {result.get('message', '')}")
else:
lines.append("✗ REBIND FAILED")
lines.append(f" {result.get('message', 'Unknown error')}")
if result.get("verified"):
lines.append("")
lines.append("✓ Verification: Report is now connected to the new model")
elif result.get("verified") is False:
lines.append("")
lines.append("⚠ Verification: Could not confirm rebind (may need time to propagate)")
if result.get("previous_model"):
lines.append("")
lines.append(f"Previous Model ID: {result['previous_model']}")
lines.append("")
lines.append("=" * 60)
output = "\n".join(lines)
if output_file:
with open(output_file, "w", encoding="utf-8") as f:
f.write(output)
print(f"Result written to: {output_file}", file=sys.stderr)
else:
print(output)
def main():
parser = argparse.ArgumentParser(
description="Rebind report to different semantic model",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Examples:
python rebind_report.py Production.Workspace/SalesReport.Report --model Production.Workspace/NewSales.SemanticModel
python rebind_report.py MyWorkspace/Report --model MyWorkspace/Model -f json
python rebind_report.py Dev.Workspace/TestReport.Report --model Prod.Workspace/ProdModel.SemanticModel --verify
"""
)
parser.add_argument(
"report",
help="Report path (e.g., 'MyWorkspace.Workspace/MyReport.Report')"
)
parser.add_argument(
"--model",
required=True,
help="New semantic model path (e.g., 'MyWorkspace.Workspace/MyModel.SemanticModel')"
)
parser.add_argument(
"-f", "--format",
choices=["text", "json"],
default="text",
help="Output format (default: text)"
)
parser.add_argument(
"-o", "--output",
help="Output file path (default: stdout)"
)
parser.add_argument(
"--verify",
action="store_true",
help="Verify rebind was successful after operation"
)
parser.add_argument(
"-v", "--verbose",
action="store_true",
help="Enable verbose output"
)
parser.add_argument(
"--dry-run",
action="store_true",
help="Show what would be done without making changes"
)
args = parser.parse_args()
# Parse report path
report_workspace, report_name = parse_item_path(args.report, "Report")
if not report_workspace or not report_name:
print(f"Error: Invalid report path: {args.report}", file=sys.stderr)
print("Expected format: Workspace.Workspace/Report.Report", file=sys.stderr)
return 1
report_path = f"{report_workspace}/{report_name}"
# Parse model path
model_workspace, model_name = parse_item_path(args.model, "SemanticModel")
if not model_workspace or not model_name:
print(f"Error: Invalid model path: {args.model}", file=sys.stderr)
print("Expected format: Workspace.Workspace/Model.SemanticModel", file=sys.stderr)
return 1
model_path = f"{model_workspace}/{model_name}"
if args.verbose:
print(f"Report: {report_path}", file=sys.stderr)
print(f"Target Model: {model_path}", file=sys.stderr)
# Check report exists
if not check_path_exists(report_path):
print(f"Error: Report does not exist: {report_path}", file=sys.stderr)
return 1
# Check model exists
if not check_path_exists(model_path):
print(f"Error: Semantic model does not exist: {model_path}", file=sys.stderr)
return 1
# Get current datasource info
if args.verbose:
print("Getting current report configuration...", file=sys.stderr)
current_datasource = get_report_datasource(report_path)
if "error" in current_datasource:
print(f"Warning: Could not get current datasource: {current_datasource['error']}", file=sys.stderr)
# Get IDs
if args.verbose:
print("Getting item IDs...", file=sys.stderr)
# Get report workspace ID and report ID
report_workspace_id = get_item_id(report_workspace)
report_id = get_item_id(report_path)
if not report_workspace_id or not report_id:
print("Error: Could not get report workspace or report ID", file=sys.stderr)
return 1
# Get model workspace ID and model ID
model_workspace_id = get_item_id(model_workspace)
model_id = get_item_id(model_path)
if not model_workspace_id or not model_id:
print("Error: Could not get model workspace or model ID", file=sys.stderr)
return 1
if args.verbose:
print(f"Report Workspace ID: {report_workspace_id}", file=sys.stderr)
print(f"Report ID: {report_id}", file=sys.stderr)
print(f"Model Workspace ID: {model_workspace_id}", file=sys.stderr)
print(f"Model ID: {model_id}", file=sys.stderr)
result = {
"report_path": report_path,
"model_path": model_path,
"report_id": report_id,
"model_id": model_id,
"previous_model": current_datasource.get("current_model"),
"status": "unknown",
"message": ""
}
# Dry run mode
if args.dry_run:
result["status"] = "dry_run"
result["message"] = "Would rebind report to specified model (dry run, no changes made)"
print_result(result, args.format, args.output)
return 0
# Perform rebind
if args.verbose:
print("Rebinding report to new model...", file=sys.stderr)
rebind_result = rebind_report(
report_workspace_id,
report_id,
model_id,
model_workspace_id if model_workspace_id != report_workspace_id else None
)
result["status"] = rebind_result["status"]
result["message"] = rebind_result["message"]
# Verify if requested
if args.verify and rebind_result["status"] == "success":
if args.verbose:
print("Verifying rebind...", file=sys.stderr)
verified = verify_rebind(report_path, model_id)
result["verified"] = verified
# Output result
print_result(result, args.format, args.output)
# Return exit code
return 0 if result["status"] == "success" else 1
if __name__ == "__main__":
sys.exit(main())
#!/usr/bin/env python3
"""
refresh_model.py - Trigger and monitor semantic model refresh
This script triggers a semantic model refresh in Microsoft Fabric and optionally
waits for completion with configurable timeout.
Usage:
python refresh_model.py <model> [--wait] [--timeout 300]
python refresh_model.py Production.Workspace/Sales.SemanticModel --wait --timeout 600
Exit codes:
0 - Refresh completed successfully (or triggered successfully without --wait)
1 - Refresh failed or error occurred
"""
import argparse
import json
import re
import subprocess
import sys
import time
from typing import Dict, Any, Optional, Tuple
def run_fab_command(args: list[str], timeout: int = 60) -> tuple[int, str, str]:
"""Execute a fab CLI command and return exit code, stdout, stderr."""
try:
result = subprocess.run(
["fab"] + args,
capture_output=True,
text=True,
timeout=timeout
)
return result.returncode, result.stdout, result.stderr
except FileNotFoundError:
return -1, "", "fab CLI not found in PATH"
except subprocess.TimeoutExpired:
return -2, "", f"Command timed out after {timeout} seconds"
except Exception as e:
return -3, "", str(e)
def parse_json_output(output: str) -> Any:
"""Parse JSON output from fab CLI."""
try:
return json.loads(output.strip())
except json.JSONDecodeError:
return None
def parse_model_path(model_path: str) -> Tuple[Optional[str], Optional[str]]:
"""Parse workspace and model name from path."""
# Handle format: Workspace.Workspace/Model.SemanticModel or Workspace/Model.SemanticModel
parts = model_path.split("/")
if len(parts) != 2:
return None, None
workspace = parts[0]
model = parts[1]
# Ensure suffixes
if not workspace.endswith(".Workspace"):
workspace = f"{workspace}.Workspace"
if not model.endswith(".SemanticModel"):
model = f"{model}.SemanticModel"
return workspace, model
def get_ids_from_path(workspace: str, model: str) -> Tuple[Optional[str], Optional[str]]:
"""Get workspace ID and model ID from names."""
# Get workspace ID
exit_code, stdout, stderr = run_fab_command(["get", workspace, "-q", "id", "-f", "json"])
if exit_code != 0:
print(f"Error: Failed to get workspace ID: {stderr}", file=sys.stderr)
return None, None
workspace_id = parse_json_output(stdout)
if not workspace_id:
print(f"Error: Could not parse workspace ID", file=sys.stderr)
return None, None
# Get model ID
model_path = f"{workspace}/{model}"
exit_code, stdout, stderr = run_fab_command(["get", model_path, "-q", "id", "-f", "json"])
if exit_code != 0:
print(f"Error: Failed to get model ID: {stderr}", file=sys.stderr)
return None, None
model_id = parse_json_output(stdout)
if not model_id:
print(f"Error: Could not parse model ID", file=sys.stderr)
return None, None
return workspace_id.strip('"'), model_id.strip('"')
def trigger_refresh(workspace_id: str, model_id: str, retry_count: int = 1) -> Dict[str, Any]:
"""Trigger semantic model refresh via Power BI API."""
result = {
"status": "unknown",
"refresh_id": None,
"polling_endpoint": None,
"message": ""
}
# Build request body
request_body = json.dumps({"retryCount": str(retry_count)})
# Trigger refresh using fab api command
api_path = f"groups/{workspace_id}/datasets/{model_id}/refreshes"
exit_code, stdout, stderr = run_fab_command([
"api", "-A", "powerbi", "-X", "post", api_path,
"--show_headers", "-i", request_body
])
if exit_code != 0:
result["status"] = "failed"
result["message"] = stderr.strip() or "Failed to trigger refresh"
return result
# Parse response
response = parse_json_output(stdout)
if not response:
result["status"] = "failed"
result["message"] = "Failed to parse API response"
return result
status_code = response.get("status_code")
if status_code != 202:
result["status"] = "failed"
result["message"] = f"Unexpected status code: {status_code}"
return result
# Extract polling endpoint from Location header
headers = response.get("headers", {})
location = headers.get("Location", "")
if location:
# Extract the API path from the full URL
match = re.search(r'https?://[^/]+/v1\.0/myorg/(.+)', location)
if match:
result["polling_endpoint"] = match.group(1).strip()
# Fallback to RequestId header
if not result["polling_endpoint"]:
request_id = headers.get("RequestId")
if request_id:
result["polling_endpoint"] = f"groups/{workspace_id}/datasets/{model_id}/refreshes/{request_id}"
result["refresh_id"] = request_id
result["status"] = "triggered"
result["message"] = "Refresh triggered successfully"
return result
def poll_refresh_status(polling_endpoint: str, timeout: int = 300, poll_interval: int = 10) -> Dict[str, Any]:
"""Poll refresh status until completion or timeout."""
result = {
"status": "unknown",
"final_status": None,
"duration_seconds": 0,
"message": ""
}
start_time = time.time()
print(f"\nPolling refresh status (timeout: {timeout}s)...")
while True:
elapsed = time.time() - start_time
if elapsed > timeout:
result["status"] = "timeout"
result["duration_seconds"] = int(elapsed)
result["message"] = f"Refresh timed out after {timeout} seconds"
return result
# Get refresh status
exit_code, stdout, stderr = run_fab_command([
"api", "-A", "powerbi", polling_endpoint, "--show_headers"
])
if exit_code != 0:
result["status"] = "error"
result["message"] = stderr.strip() or "Failed to get refresh status"
return result
response = parse_json_output(stdout)
if not response:
result["status"] = "error"
result["message"] = "Failed to parse status response"
return result
status_code = response.get("status_code")
if status_code not in [200, 202]:
result["status"] = "error"
result["message"] = f"Unexpected status code: {status_code}"
return result
# Parse the status text
status_text = response.get("text", {})
if isinstance(status_text, str):
status_text = parse_json_output(status_text) or {}
refresh_status = status_text.get("extendedStatus") or status_text.get("status", "Unknown")
print(f" Status: {refresh_status} (elapsed: {int(elapsed)}s)")
# Check for completion states
if refresh_status == "Completed":
result["status"] = "completed"
result["final_status"] = refresh_status
result["duration_seconds"] = int(time.time() - start_time)
result["message"] = "Refresh completed successfully"
return result
if refresh_status in ["Failed", "Cancelled", "Disabled", "TimedOut"]:
result["status"] = "failed"
result["final_status"] = refresh_status
result["duration_seconds"] = int(time.time() - start_time)
result["message"] = f"Refresh ended with status: {refresh_status}"
# Try to get error details
if "error" in status_text:
result["error_details"] = status_text["error"]
return result
# Still in progress, wait and poll again
time.sleep(poll_interval)
def refresh_model(
model_path: str,
wait: bool = False,
timeout: int = 300,
retry_count: int = 1
) -> Dict[str, Any]:
"""Main function to refresh a semantic model."""
result = {
"model_path": model_path,
"wait": wait,
"timeout": timeout,
"trigger_result": None,
"poll_result": None,
"overall_status": "unknown"
}
# Parse model path
workspace, model = parse_model_path(model_path)
if not workspace or not model:
result["overall_status"] = "error"
result["message"] = f"Invalid model path: {model_path}. Expected format: Workspace/Model.SemanticModel"
return result
print(f"\nModel: {workspace}/{model}")
# Get IDs
print("Resolving workspace and model IDs...")
workspace_id, model_id = get_ids_from_path(workspace, model)
if not workspace_id or not model_id:
result["overall_status"] = "error"
result["message"] = "Failed to resolve workspace or model ID"
return result
result["workspace_id"] = workspace_id
result["model_id"] = model_id
# Trigger refresh
print("Triggering refresh...")
trigger_result = trigger_refresh(workspace_id, model_id, retry_count)
result["trigger_result"] = trigger_result
if trigger_result["status"] != "triggered":
result["overall_status"] = "failed"
result["message"] = trigger_result["message"]
return result
print(f"✓ {trigger_result['message']}")
if not wait:
result["overall_status"] = "triggered"
result["message"] = "Refresh triggered. Use --wait to monitor completion."
return result
# Poll for completion
if not trigger_result.get("polling_endpoint"):
result["overall_status"] = "warning"
result["message"] = "Refresh triggered but no polling endpoint available"
return result
poll_result = poll_refresh_status(
trigger_result["polling_endpoint"],
timeout=timeout
)
result["poll_result"] = poll_result
if poll_result["status"] == "completed":
result["overall_status"] = "completed"
result["message"] = f"Refresh completed in {poll_result['duration_seconds']} seconds"
else:
result["overall_status"] = "failed"
result["message"] = poll_result["message"]
return result
def main():
parser = argparse.ArgumentParser(
description="Trigger and monitor semantic model refresh",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Examples:
python refresh_model.py Production.Workspace/Sales.SemanticModel
python refresh_model.py Production/Sales.SemanticModel --wait
python refresh_model.py Dev.Workspace/Model.SemanticModel --wait --timeout 600
python refresh_model.py "My Workspace/My Model.SemanticModel" --wait --json
Note:
Without --wait, the script triggers the refresh and exits immediately.
With --wait, the script polls until completion or timeout.
"""
)
parser.add_argument(
"model",
help="Semantic model path (format: Workspace/Model.SemanticModel)"
)
parser.add_argument(
"--wait", "-w",
action="store_true",
help="Wait for refresh to complete"
)
parser.add_argument(
"--timeout", "-t",
type=int,
default=300,
help="Timeout in seconds when waiting (default: 300)"
)
parser.add_argument(
"--retry-count", "-r",
type=int,
default=1,
help="Number of retries for the refresh operation (default: 1)"
)
parser.add_argument(
"--json", "-j",
action="store_true",
help="Output results in JSON format"
)
args = parser.parse_args()
try:
result = refresh_model(
model_path=args.model,
wait=args.wait,
timeout=args.timeout,
retry_count=args.retry_count
)
# Print summary
print("\n" + "=" * 50)
print("REFRESH RESULT")
print("=" * 50)
print(f"Model: {result['model_path']}")
print(f"Status: {result['overall_status'].upper()}")
print(f"Message: {result.get('message', 'N/A')}")
if result.get("poll_result"):
print(f"Duration: {result['poll_result'].get('duration_seconds', 'N/A')} seconds")
print("=" * 50)
if args.json:
print("\n" + json.dumps(result, indent=2))
# Exit code based on status
success_statuses = ["completed", "triggered"]
sys.exit(0 if result["overall_status"] in success_statuses else 1)
except Exception as e:
print(f"Error: {e}", file=sys.stderr)
sys.exit(1)
if __name__ == "__main__":
main()
Related skills
FAQ
What Power BI operations are supported?
Semantic models, reports, DAX queries, dataset refresh, and gateway management.
What CLI is used?
Microsoft Fabric CLI with Power BI-specific command patterns.
When does this skill activate?
When users work with Power BI items, refresh datasets, or execute DAX via Fabric.
Is Fabric Cli Powerbi safe to install?
skills.sh reports 2 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.