Skip to content

Deploy a native semantic model

Deploy native PBIP/TMDL, query a measure, then hide relationship foreign keys with one modelling rule. Weaver applies the rule to the deployed definition without editing the native files.

Native files first

A PBIP project contains the model’s native TMDL and its Report definitions. You can also author a model directly in a named TMDL file; native project layout describes both forms.

Before you start

Install Weaver. This exercise begins in Power BI-only mode, without a Catalogue. Use an existing disposable Power BI service workspace with licensing and permissions to deploy, read and refresh Import Semantic Models, and to edit the linked Report for the field-list check. Use existing permissions; this exercise does not require changing tenant settings. Service requirements cover the licensing and API boundaries.

Parcel includes Delivery and Depot tables, a relationship and a Delivery Count measure. Its ParcelStatus table has the same Parcel ID, Status and Depot columns as the Warehouse data, with two sample rows to start. The model needs no Lakehouse or source Warehouse workload. If you already have a parcel project from the Fabric tasks, run the command from inside it with --project-folder . instead; Initialise keeps your data items and adds the models.

1. Choose the destination

weaver initialise \
  --workspace "Parcel Development" \
  --project-folder ./parcel \
  --semantic-model Parcel \
  --example Parcel
cd parcel
from pathlib import Path

import weaver

project = Path("builtin") / "project"
initialise_result = weaver.initialise(
    project_folder=project,
    workspace='Parcel Development',
    semantic_model='Parcel',
    example='Parcel',
)
initialise_result.report()

Initialise writes the native model and linked Report under PowerBI/Parcel/, a workspace configuration and supporting project files. In the Terminal, choose 1 to confirm. It creates no Catalogue Warehouse or Fabric Environment. Build creates the Power BI items; building and refreshing this inline model do not require Spark. You can later add Fabric data workloads and a Catalogue to this same project without replacing its native definitions or existing bindings.

Open PowerBI/Parcel/Parcel.pbip in Desktop to inspect the native project. Open PowerBI/Parcel/Parcel.SemanticModel/definition/tables/Delivery.tmdl in your editor. The generated table includes the sample partition and measure:

/// Three parcel events, including two delivered parcels.
table Delivery
    measure 'Delivery Count' = SUM(Delivery[Delivered])
        formatString: 0
    measure InternalCost = 7
        formatString: 0.00
    column ParcelId
        dataType: int64
        sourceColumn: ParcelId
        summarizeBy: none
    column DepotId
        dataType: int64
        sourceColumn: DepotId
        summarizeBy: none
    column Delivered
        dataType: int64
        sourceColumn: Delivered
        summarizeBy: sum
    partition Delivery = m
        mode: import
        source =
            let
                Source = #table(type table [ParcelId = Int64.Type, DepotId = Int64.Type, Delivered = Int64.Type], {{1, 10, 1}, {2, 10, 1}, {3, 20, 0}})
            in
                Source

ParcelStatus.tmdl, in the same native tables folder, carries the Warehouse-shaped sample. Later, add Weaver.Source to connect that table to the Warehouse without changing its columns or the linked Report.

workspace-config.yml already binds SemanticModel/Parcel and Report/Parcel to their physical names. Keep any existing source bindings. Environment mappings covers choosing other physical destinations without changing the source.

2. Check the project

weaver check

Check reads local declarations and definitions. It does not execute M/DAX, deploy items or test service permissions.

3. Deploy the native model

weaver build --item SemanticModel/Parcel

The command returned:

Terminal output exit 0
Workspace  Parcel Development
Catalogue  none
Targets
  SemanticModel/Parcel


Build

    Create SemanticModel/Parcel                                                                           20.7s
  Create Power BI items                                                                                   20.7s
  Resolve semantic data sources                                                                            0.0s
    Resolve SemanticModel/Parcel                                                                           0.0s
  Read target inventories                                                                                  0.2s
  Read semantic sources                                                                                    0.0s
  Prepare bundle                                                                                           0.0s
Install selection
  SemanticModel/Parcel
    changed                 1
    selected for build      1

→   SemanticModel/Parcel · Deploy semantic definitions · 1 action
✓   SemanticModel/Parcel · Deploy semantic definitions · 1 action                                         21.3s
→   SemanticModel/Parcel · Verify deployed semantic definitions · 1 action
✓   SemanticModel/Parcel · Verify deployed semantic definitions · 1 action                                20.5s
  Install                                                                                                 41.8s
✓ Build                                                                                                   1m02s

Installation
    2 succeeded
    0 failed
    0 skipped
  Report  [local-path]
  Bundle  01a374b67d8eb3b7035bf9ecbd1bb768e263e59c19cbf07741169e18d6b83a5f

from pathlib import Path

import weaver

project = Path("builtin") / "project"
build_result = weaver.build(
    source=project,
    items=['SemanticModel/Parcel'],
    workspace='Parcel Development',
)
build_result.report()

The command returned:

Notebook output
{'source': '.',
 'items': ['SemanticModel/Parcel'],
 'bundle_id': '[bundle-id]',
 'installation': True,
 'bundle_path': None,
 'status': 'succeeded',
 'errors': [],
 'actions': {'total': 2, 'succeeded': 2, 'skipped': 0, 'failed': 0},
 'report_path': '[local-path]'}

This selects only the inline semantic model, even if your project also has source-backed models. Find Parcel in your workspace. The Report stays local until you deploy it.

4. Refresh and inspect the model

weaver load SemanticModel/Parcel

The command returned:

Terminal output exit 0
Load

  Resolve semantic models                                                                                  0.5s
  Read catalogue                                                                                           0.0s
Workspace  Parcel Development
Catalogue  none
Targets
  SemanticModel/Parcel

  Build run graph                                                                                          0.0s
→   Load SemanticModel/Parcel
✓   Load SemanticModel/Parcel                                                                              5.0s
  Execute                                                                                                  5.0s
✓ Load                                                                                                     5.6s

load succeeded: SemanticModel/Parcel

Load summary
    1 succeeded
    0 failed
    0 blocked

from pathlib import Path

import weaver

project = Path("builtin") / "project"
load_result = weaver.load(
    items=['SemanticModel/Parcel'],
    source=project,
    workspace='Parcel Development',
)
load_result.report()

The command returned:

Notebook output
{'requested': ['SemanticModel/Parcel'],
 'status': 'succeeded',
 'dry_run': False,
 'fault_tolerant': False,
 'reload': False,
 'ignore_stability_threshold': False,
 'workspace': 'Parcel Development',
 'workflow_id': None,
 'started_at': '2026-10-17T07:53:44.405479+00:00',
 'finished_at': '2026-10-17T07:53:49.090971+00:00',
 'order': ['load:SemanticModel/Parcel'],
 'edges': [],
 'nodes': [{'node_id': 'load:SemanticModel/Parcel',
            'logical_id': 'SemanticModel/Parcel',
            'physical_target': 'SemanticModel/Parcel',
            'primitive_kind': 'semantic_refresh',
            'dispatch_location': 'groups/[item-id]/datasets/[item-id]/refreshes',
            'status': 'succeeded',
            'executed': True,
            'started_at': '2026-10-17T07:53:44.407825+00:00',
            'finished_at': '2026-10-17T07:53:49.090583+00:00',
            'rows': {'status': 'Completed',
                     'request_id': '[item-id]',
                     'start_time': '2026-10-17T07:53:44.66',
                     'end_time': '2026-10-17T07:53:47.62',
                     'error_message': None,
                     'succeeded': True},
            'messages': []}],
 'messages': []}

In the workspace, open the deployed Parcel semantic model and choose Write DAX queries. Run:

EVALUATE
ROW("DeliveryCount", [Delivery Count])

The live query returned DeliveryCount = 2. DAX query view needs model write permission and the workspace’s existing web modelling setting. If it is unavailable, connect Power BI Desktop to the published Parcel semantic model using a live connection and run the query in Desktop’s DAX query view with Read and Build permission. Do not query the local PBIP instead: you are checking the deployed model.

Use the linked Report already generated by Initialise for the field-list check below; there is no need to create another Report.

AutoHideForeignKeys: hide relationship foreign keys

Add the same-name overlay

Create PowerBI/Parcel/Parcel.tmdl:

model Model
    annotation 'Weaver.AutoHideForeignKeys' = true

The filename matches Parcel.SemanticModel, so it refines that model rather than declaring another one.

Deploy and inspect

Build the model and its existing linked Report together. Without a Catalogue, the linked model must be selected in the same Build:

weaver check
weaver build --item SemanticModel/Parcel --item Report/Parcel

The command returned:

Terminal output exit 0
Workspace  Parcel Development
Catalogue  none
Targets
  SemanticModel/Parcel
  Report/Parcel


Build

    Create Report/Parcel                                                                                  20.6s
  Create Power BI items                                                                                   20.6s
  Resolve semantic data sources                                                                            0.0s
    Resolve SemanticModel/Parcel                                                                           0.0s
    Resolve Report/Parcel                                                                                  0.0s
  Read target inventories                                                                                  0.2s
  Read semantic sources                                                                                    0.0s
  Prepare bundle                                                                                           0.0s
Install selection
  SemanticModel/Parcel
    changed                 1
    selected for build      1
  Report/Parcel
    changed                 1
    selected for build      1

→   SemanticModel/Parcel · Deploy semantic definitions · 1 action
✓   SemanticModel/Parcel · Deploy semantic definitions · 1 action                                         21.0s
→   SemanticModel/Parcel, Report/Parcel · Verify deployed semantic definitions · 1 action
✓   SemanticModel/Parcel, Report/Parcel · Verify deployed semantic definitions · 1 action                 20.5s
→   Report/Parcel · Deploy Report definitions · 1 action
✓   Report/Parcel · Deploy Report definitions · 1 action                                                  20.4s
→   Report/Parcel · Verify deployed Report definitions · 1 action
✓   Report/Parcel · Verify deployed Report definitions · 1 action                                         20.9s
  Install                                                                                                 1m22s
✓ Build                                                                                                   1m43s

Installation
    4 succeeded
    0 failed
    0 skipped
  Report  [local-path]
  Bundle  ca0aeddd18fafaf4a715693285d9d933b0dd6b057bb7feb7588e13780e3e8f21
weaver load SemanticModel/Parcel

The command returned:

Terminal output exit 0
Load

  Resolve semantic models                                                                                  0.5s
  Read catalogue                                                                                           0.0s
Workspace  Parcel Development
Catalogue  none
Targets
  SemanticModel/Parcel

  Build run graph                                                                                          0.0s
→   Load SemanticModel/Parcel
✓   Load SemanticModel/Parcel                                                                              7.3s
  Execute                                                                                                  7.3s
✓ Load                                                                                                     7.8s

load succeeded: SemanticModel/Parcel

Load summary
    1 succeeded
    0 failed
    0 blocked

from pathlib import Path

import weaver

project = Path("builtin") / "project"
load_result = weaver.load(
    items=['SemanticModel/Parcel'],
    source=project,
    workspace='Parcel Development',
)
load_result.report()

The command returned:

Notebook output
{'requested': ['SemanticModel/Parcel'],
 'status': 'succeeded',
 'dry_run': False,
 'fault_tolerant': False,
 'reload': False,
 'ignore_stability_threshold': False,
 'workspace': 'Parcel Development',
 'workflow_id': None,
 'started_at': '2026-10-17T07:53:49.465915+00:00',
 'finished_at': '2026-10-17T07:53:54.149703+00:00',
 'order': ['load:SemanticModel/Parcel'],
 'edges': [],
 'nodes': [{'node_id': 'load:SemanticModel/Parcel',
            'logical_id': 'SemanticModel/Parcel',
            'physical_target': 'SemanticModel/Parcel',
            'primitive_kind': 'semantic_refresh',
            'dispatch_location': 'groups/[item-id]/datasets/[item-id]/refreshes',
            'status': 'succeeded',
            'executed': True,
            'started_at': '2026-10-17T07:53:49.467602+00:00',
            'finished_at': '2026-10-17T07:53:54.149342+00:00',
            'rows': {'status': 'Completed',
                     'request_id': '[item-id]',
                     'start_time': '2026-10-17T07:53:49.723',
                     'end_time': '2026-10-17T07:53:53.923',
                     'error_message': None,
                     'succeeded': True},
            'messages': []}],
 'messages': []}

In the Power BI service, open the generated Parcel Report in Edit mode and expand Delivery and Depot in the Data pane. Delivery[DepotId] is gone from the field list; Depot[DepotId] is still there. Reading the deployed definition back confirms the change:

Check Before the rule After the rule
Delivery[DepotId] Visible Hidden
Depot[DepotId] Visible Visible
Delivery Count 2 2

The native files and inline data remain in place. Desktop does not execute Weaver annotations when opening the local PBIP; inspect the service-connected model for the change. Hidden fields are not a security policy or RLS/OLS.

To stop requesting the transformation, remove the annotation and rebuild. Setting it to false is a no-op, not an instruction to undo an independently authored native hide.

Inspect the native relationship

Inspect the relationship the rule uses

Open PowerBI/Parcel/Parcel.SemanticModel/definition/relationships.tmdl:

relationship Delivery_Depot
    fromColumn: Delivery.DepotId
    toColumn: Depot.DepotId
    fromCardinality: many
    toCardinality: one
    crossFilteringBehavior: oneDirection

The rule hides the many-side key from the field list, not the one-side key.

Carry the convention to the next model

When this convention belongs on several models, share it in PowerBI/policy.tmdl. The next task keeps Parcel and its local overlay, adding common formatting and a calculation group.

Validate the model to check its number, or connect a managed Fabric source by adding a source-backed model alongside this inline model. Weaver documents explains how overlays fit beside native definitions.