|
| 1 | +--- |
| 2 | +title: Fabric-CICD Updates - Semantic Models, Parameters and Config |
| 3 | +description: A look at recent updates to fabric-cicd including semantic model binding, response collection, and YAML configuration |
| 4 | +image: /assets/images/blog/2026/2026-02-02-Fabric-cicd-0-1-33/fabric-cicd.png |
| 5 | +date: 2026-02-02 |
| 6 | +authors: |
| 7 | + - jDuddy |
| 8 | +comments: true |
| 9 | +categories: |
| 10 | + - CICD |
| 11 | +links: |
| 12 | + - fabric-cicd Docs: https://microsoft.github.io/fabric-cicd/latest/ |
| 13 | +slug: posts/Fabric-cicd-0-1-33 |
| 14 | +--- |
| 15 | + |
| 16 | +I last posted about [fabric-cicd](https://microsoft.github.io/fabric-cicd/latest/) in May 2025. Since then, there have been significant updates to the tool, particularly regarding configuration based deployments, cross-workspace deployments, and Semantic Model binding. In this post, I'll cover the notable changes for Power BI deployments from release v0.1.17 through v0.1.34. |
| 17 | + |
| 18 | +## Configuration Deployment |
| 19 | + |
| 20 | +As of **v0.1.26**, fabric-cicd supports alternative deployment mode: [Configuration-based deployment](https://microsoft.github.io/fabric-cicd/0.1.33/how_to/config_deployment/). Instead of defining a workspace object, with parameters and calling `#!py publish_all_items()`/`#!py unpublish_all_orphan_items()`, you can define and pass `config.yml` to `#!pydeploy_with_config()`. With `config.yml` you can define workspaces, item types in scope, publish and unpublish rules etc. |
| 21 | + |
| 22 | +!!! "Optional Features" |
| 23 | + Please note selective publish/unpublish requires you enable [Feature Flags](https://microsoft.github.io/fabric-cicd/0.1.33/how_to/optional_feature/) |
| 24 | + |
| 25 | +=== "Config.yml" |
| 26 | + |
| 27 | + ```yml title="config.yml" |
| 28 | + core: |
| 29 | + workspace: |
| 30 | + dev: "Fabric-Dev-Engineering" |
| 31 | + test: "Fabric-Test-Engineering" |
| 32 | + prod: "Fabric-Prod-Engineering" |
| 33 | + |
| 34 | + workspace_id: |
| 35 | + dev: "8b6e2c7a-4c1f-4e3a-9b2e-7d8f2e1a6c3b" |
| 36 | + test: "2f4b9e8d-1a7c-4d3e-b8e2-5c9f7a2d4e1b" |
| 37 | + prod: "7c3e1f8b-2d4a-4b9e-8f2c-1a6c3b7d8e2f" |
| 38 | + |
| 39 | + repository_directory: "." # relative path |
| 40 | + |
| 41 | + item_types_in_scope: |
| 42 | + - Notebook |
| 43 | + - DataPipeline |
| 44 | + - Environment |
| 45 | + - Lakehouse |
| 46 | + |
| 47 | + parameter: "parameter.yml" # relative path |
| 48 | + |
| 49 | + publish: |
| 50 | + # Don't publish items matching this pattern |
| 51 | + exclude_regex: "^DONT_DEPLOY.*" |
| 52 | + |
| 53 | + folder_exclude_regex: "^DONT_DEPLOY_FOLDER/" |
| 54 | + |
| 55 | + items_to_include: |
| 56 | + - "Hello World.Notebook" |
| 57 | + - "Run Hello World.DataPipeline" |
| 58 | + |
| 59 | + skip: |
| 60 | + dev: true |
| 61 | + test: false |
| 62 | + prod: false |
| 63 | + |
| 64 | + unpublish: |
| 65 | + # Don't unpublish items matching this pattern |
| 66 | + exclude_regex: "^DEBUG.*" |
| 67 | + |
| 68 | + skip: |
| 69 | + dev: false |
| 70 | + test: false |
| 71 | + prod: true |
| 72 | + |
| 73 | + features: |
| 74 | + - enable_shortcut_publish |
| 75 | + - enable_experimental_features |
| 76 | + - enable_items_to_include |
| 77 | + |
| 78 | + constants: |
| 79 | + DEFAULT_API_ROOT_URL: "https://api.fabric.microsoft.com" |
| 80 | + ``` |
| 81 | + |
| 82 | +=== "Deployment" |
| 83 | + |
| 84 | + ```py |
| 85 | + from fabric_cicd import deploy_with_config |
| 86 | + |
| 87 | + # Deploy using a config file |
| 88 | + deploy_with_config( |
| 89 | + config_file_path="path/to/config.yml", # required |
| 90 | + environment="dev" # optional (recommended) |
| 91 | + ) |
| 92 | + ``` |
| 93 | + |
| 94 | +## Parameter File Templates |
| 95 | + |
| 96 | +As your project grows, your `parameter.yml` file can become large and unwieldy. To help with organization and reuse, **v0.1.31** introduced support for [Parameter File Templates](https://microsoft.github.io/fabric-cicd/0.1.33/how_to/parameterization/?h=extend#parameter-file-templates). You can use the `extend` key to import rules from other YAML files. |
| 97 | + |
| 98 | +=== "Repo" |
| 99 | + |
| 100 | + ```txt |
| 101 | + . |
| 102 | + ├── 📁 WorkspaceFoo |
| 103 | + │ ├── 📁 Bar.SemanticModel |
| 104 | + │ ├── 📁 Bar.Report |
| 105 | + │ └── 📄 parameter.yml |
| 106 | + ├── 📁 templates |
| 107 | + │ └── 📄 base_parameters.yml |
| 108 | + └ .gitignore |
| 109 | + ``` |
| 110 | + |
| 111 | +=== "parameters.yml" |
| 112 | + |
| 113 | + ```yaml title="parameters.yml" |
| 114 | + extend: |
| 115 | + - "./templates/base.yml" |
| 116 | + |
| 117 | + find_replace: |
| 118 | + # Lakehouse Connection Guid |
| 119 | + - find_value: "db52be81-c2b2-4261-84fa-840c67f4bbd0" |
| 120 | + replace_value: |
| 121 | + PPE: "81bbb339-8d0b-46e8-bfa6-289a159c0733" |
| 122 | + PROD: "5d6a1b16-447f-464a-b959-45d0fed35ca0" |
| 123 | + # Optional fields: |
| 124 | + item_type: "Notebook" |
| 125 | + item_name: ["Hello World", "Hello World Subfolder"] |
| 126 | + file_path: |
| 127 | + - "/Hello World.Notebook/notebook-content.py" |
| 128 | + - "/subfolder/Hello World Subfolder.Notebook/notebook-content.py" |
| 129 | + |
| 130 | + spark_pool: |
| 131 | + # CapacityPool_Large |
| 132 | + - instance_pool_id: "72c68dbc-0775-4d59-909d-a47896f4573b" |
| 133 | + replace_value: |
| 134 | + PPE: |
| 135 | + type: "Capacity" |
| 136 | + name: "CapacityPool_Large_PPE" |
| 137 | + PROD: |
| 138 | + type: "Capacity" |
| 139 | + name: "CapacityPool_Large_PROD" |
| 140 | + # Optional field: |
| 141 | + item_name: "World" |
| 142 | + ``` |
| 143 | + |
| 144 | +=== "base_parameter.yml" |
| 145 | + |
| 146 | + ```yaml title="base_parameter.yml" |
| 147 | + find_replace: |
| 148 | + # Lakehouse Connection Guid regex |
| 149 | + - find_value: \#\s*META\s+"default_lakehouse":\s*"([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})" |
| 150 | + replace_value: |
| 151 | + # Variable: $items.type.name.attribute (Note: item type and name values are CASE SENSITIVE; id attribute returns the deployed item's id/guid) |
| 152 | + PPE: "$items.Lakehouse.WithoutSchema.id" |
| 153 | + PROD: "$items.Lakehouse.WithoutSchema.id" |
| 154 | + # Optional fields: |
| 155 | + is_regex: "true" |
| 156 | + file_path: "/Example Notebook.Notebook/notebook-content.py" |
| 157 | + # Lakehouse workspace id regex |
| 158 | + - find_value: \#\s*META\s+"default_lakehouse_workspace_id":\s*"([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})" |
| 159 | + replace_value: |
| 160 | + # Variable: $workspace.id -> target workspace id |
| 161 | + PPE: "$workspace.id" |
| 162 | + PROD: "$workspace.id" |
| 163 | + # Optional fields: |
| 164 | + is_regex: "true" |
| 165 | + file_path: "/Example Notebook.Notebook/notebook-content.py" |
| 166 | + ``` |
| 167 | + |
| 168 | +## Dynamic Parameterization |
| 169 | + |
| 170 | +Static replacements are great, but sometimes you need values that are only known at deployment time—like the Workspace ID of the environment you are deploying to, or the Item ID of a Lakehouse that was just created. |
| 171 | + |
| 172 | +The [Dynamic Replacement](https://microsoft.github.io/fabric-cicd/latest/how_to/parameterization/#dynamic-replacement) feature allows you to use variables in your `parameter.yml` that get resolved during deployment. |
| 173 | + |
| 174 | +**Workspace Variables:** |
| 175 | + |
| 176 | +| Variable | Description | |
| 177 | +| :--- | :--- | |
| 178 | +| `$workspace.id` | The target workspace ID (synonym: `$workspace.$id`) | |
| 179 | +| `$workspace.<Name>` | The ID of the workspace with the specified name | |
| 180 | + |
| 181 | +**Item Attributes:** |
| 182 | + |
| 183 | +You can access these attributes using the pattern `$items.<ItemType>.<ItemName>.<Attribute>`. |
| 184 | + |
| 185 | +| Attribute | Description | |
| 186 | +| :--- | :--- | |
| 187 | +| `$id` | The Item ID (Guid) | |
| 188 | +| `$sqlendpoint` | The SQL Endpoint string | |
| 189 | +| `$sqlendpointid` | The SQL Endpoint Item ID | |
| 190 | +| `$queryserviceuri` | The Query Service URI | |
| 191 | + |
| 192 | +Since **v0.1.29**, you can also reference items in *other* workspaces. This is essential for the "Thin Report" pattern, where reports in business workspaces connect to a central "Gold" Semantic Model. |
| 193 | + |
| 194 | +* `$workspace.<WorkspaceName>.$items.<ItemType>.<ItemName>.$id` |
| 195 | + |
| 196 | +For example, we can bind a "Thin Report" to a Semantic Model in another workspace: |
| 197 | + |
| 198 | +```yaml |
| 199 | +find_replace: |
| 200 | + # Find the existing dataset ID in the report definition |
| 201 | + - find_value: "00000000-0000-0000-0000-000000000000" |
| 202 | + replace_value: |
| 203 | + # Dynamically get the ID of semantic model (Bar) from the Foo workspace |
| 204 | + TEST: "$workspace.Foo_UAT.$items.SemanticModel.Bar.$id" |
| 205 | + PROD: "$workspace.Foo_Prod.$items.SemanticModel.Bar.$id" |
| 206 | + # Apply only to the relevant file |
| 207 | + file_path: "Foo.Report/definition.pbir" |
| 208 | +``` |
| 209 | +
|
| 210 | +## key_value_replace Support |
| 211 | +
|
| 212 | +Additionally, parameter handling has become more robust. In **v0.1.33**, `key_value_replace` support was added to YAML configurations, allowing for precise key-based replacements in your JSON definitions. |
| 213 | + |
| 214 | +```yaml Title = parameter.yml |
| 215 | +key_value_replace: |
| 216 | + # Example: Replace Server in Semantic Model "Foo" for DataSource "Bar" |
| 217 | + - find_key: $.model.dataSources[?(@.name=="Bar")].connectionDetails.server |
| 218 | + replace_value: |
| 219 | + PPE: "server-uat" |
| 220 | + PROD: "server-prod" |
| 221 | + item_type: "SemanticModel" |
| 222 | + item_name: "Foo" |
| 223 | +``` |
| 224 | + |
| 225 | +## Response Collection |
| 226 | + |
| 227 | +The **Dynamic Parameterization** capabilities discussed above, specifically the ability to resolve variables like `$items...` and cross-workspace references, are powered by the `response_collection` feature. |
| 228 | + |
| 229 | +Introduced in **v0.1.29**, this mechanism captures the API responses during the deployment process. When you use `deploy_with_config`, the tool automatically uses these responses to populate the dynamic variables. |
| 230 | + |
| 231 | +If you are maintaining custom Python deployment scripts, you can also leverage this feature directly to handle complex dependencies, such as retrieving the specific ID of a newly deployed Notebook to pass into a pipeline. |
| 232 | + |
| 233 | +```python |
| 234 | +from fabric_cicd import append_feature_flag, FabricWorkspace, publish_all_items |
| 235 | +
|
| 236 | +# Enable the feature flag to capture API outputs |
| 237 | +append_feature_flag("enable_response_collection") |
| 238 | +
|
| 239 | +workspace = FabricWorkspace(workspace_id="...", ...) |
| 240 | +responses = publish_all_items(workspace) |
| 241 | +
|
| 242 | +# Access all responses |
| 243 | +print(responses) |
| 244 | +
|
| 245 | +# Access individual item responses |
| 246 | +notebook_response = workspace.responses["Notebook"]["Hello World"] |
| 247 | +``` |
| 248 | + |
| 249 | +## Semantic Model Binding |
| 250 | + |
| 251 | +[Semantic Model Binding](https://microsoft.github.io/fabric-cicd/latest/how_to/parameterization/#semantic_model_binding) allows you to map your Semantic Models to specific connections, removing additional clicks after a new semantic model is deployed, or new sources are added to a existing semantic model. |
| 252 | + |
| 253 | +```yml Title="parameters.yml" |
| 254 | +semantic_model_binding: |
| 255 | + # Required field: value must be a string (GUID) |
| 256 | + # Connection Ids can be found from the Fabric UI under Settings -> Manage Connections and gateways -> Settings pane of the connection |
| 257 | + - connection_id: <connection_id> |
| 258 | + # Required field: value must be a string or a list of strings |
| 259 | + semantic_model_name: <semantic_model_name> |
| 260 | + # OR |
| 261 | + semantic_model_name: [<semantic_model_name1>,<semantic_model_name2>,...] |
| 262 | +``` |
| 263 | + |
| 264 | +Support for binding to **On-Premises Gateways** was added in **v0.1.30**, and support for parameterized **multiple connections** within a single model arrived in **v0.1.33**. |
0 commit comments