Skip to content

parameter.yml

fabric-cicd rewrites environment-specific values with parameter.yml in each workspace folder. Its config.yml names the parameter file; allez resolves any $allez.* variables before fab deploy reads it.

The repo structure shows where the file and its optional templates belong.

Every environment-specific value lives here. fabric-cicd treats the file as optional; every solution needs one. Four rule types:

Prefer dynamic variables in replace_value over ids typed by hand: $workspace.$id, $items.<Type>.<Name>.$id, $items.<Type>.<Name>.$sqlendpoint.

fab deploy checks no coverage

The mechanism is find-and-replace. A missed value deploys green and fails at run time in the wrong workspace, and a rule that matches nothing is not an error to fabric-cicd. Environment-specific ids hide in notebook-content.py (default lakehouse and warehouse bindings, %run targets), in SparkJobDefinition compute properties, and in TMDL expressions. allez audit finds them offline: an id no rule rewrites fails it, a rule that matches nothing warns, and the example's validate.yml runs it on every feature push and as a pull request check.

An is_regex pattern must contain exactly one capture group; (?:…) groups don't count. Otherwise fabric-cicd rejects the whole file, before any item filter applies. Only the captured group is replaced by replace_value; the rest of the match stays. Anchor the pattern on something only the right value has. A bare "id":\s*"(…)" in a notebook's metadata also matches a lakehouse or Environment item id, and rewrites it with the wrong id.

Match the structure around the id with a regex and let the capture group take the value. A literal id stops matching after the first commit-back, once the branch's own ids are committed:

fabric/data/parameter.yml
# Wrong — stale after the first commit-back, silently
- find_value: "8b6e2c7a-4c1f-4e3a-9b2e-7d8f2e1a6c3b"

# Right — matches whatever GUID sits in that spot, on any branch
- find_value: 'expression DataWorkspaceId = "([0-9a-fA-F-]{36})"'
  is_regex: "true"
  replace_value:
    _ALL_: "$workspace.$id"

The same applies on the replace side: use a dynamic value or a $allez.* token rather than an id copied by hand.

A schedule can be parameterised too. An item's .schedules file is part of its definition, tracked in git and deployed by fab deploy, so a rule can adjust it per environment. Fabric exports a schedule in two shapes and accepts no other. Enabled is "enabled": true, "enabledControlType": "UserEnabled". Disabled is "enabled": false with no second key. Writing "enabled": false beside UserEnabled fails the item create with a 500. So the one capture group spans both keys, and each environment writes a whole shape:

fabric/data/parameter.yml
- find_value: '("enabled":\s*(?:true|false)(?:,\s*"enabledControlType":\s*"[A-Za-z]+")?)'
  is_regex: "true"
  replace_value:
    feature: '"enabled": false'
    dev: '"enabled": false'
    test: '"enabled": false'
    prod: '"enabled": true, "enabledControlType": "UserEnabled"'
  item_type: "Notebook"
  file_path: "/Notebook.Notebook/.schedules"

Which mechanism for a cross-env reference

Work down the list:

  1. fabric-cicd dynamic variables. Two forms: the target workspace ($workspace.$id, $workspace.$name, $items.<Type>.<Name>.$id), and another workspace by literal display name ($workspace.<name>.$id, $workspace.<name>.$items.<Type>.<Name>.$id). The named thing must already exist, so deploy order matters. Right for a workspace this repo doesn't own.
  2. $allez.* tokens, when the answer can't be a literal name: a workspace this repo owns. A token is keyed by sub-folder, so it follows --env and the branch. An item in it gives $id, and a warehouse, lakehouse, mirrored database or SQL database gives $sqlendpoint and $sqlendpointid too, the same values as fabric-cicd's.
  3. fabric-cicd literal per-environment values in replace_value, for what none of these can name. A connection id is one: it is the same across the whole tenant, so a literal stays correct.

Variable libraries are not recommended for cross-environment references

A variable library is not on the list, and I don't recommend one for a reference. An item reference in a library is a raw workspace id and item id, and Fabric refuses a library whose reference names an item that does not exist (ReferencedEntityNotFound). So the library cannot be published into a new environment, or into a long-lived branch workspace that lacks the item, until something writes the right id into it. That something is a rule from the list above, usually a $allez token, which could have rewritten the consuming item directly. Technically, a cross-workspace reference for a workspace earlier in the dag would be fine, but it adds complexity with no benefit.

For a plain value that is not an id, a library is fine, and allez deploy publishes it like any other item. After publishing it, fabric-cicd sets the active value set with a PATCH to variableLibraries/<id>: the set whose name matches the environment (--env, or feature for a branch workspace), or Default value set with a warning when none matches. So name each value set after an environment. The audit checks a library's rules and its publish order like any other item's.

Splitting a large parameter.yml

A parameter.yml that has grown too large can be split into extend templates. List them under an extend: key in the main file, with paths relative to that file:

fabric/data/parameter.yml
extend:
  - "./.deploy/parameters/notebooks.yml"
  - "./.deploy/parameters/pipelines.yml"

find_replace:
  # rules that stay in the main file

fab deploy reads and merges them. allez passes the sub-folder unchanged, so this needs no flag and no config.yml change. $allez.* tokens resolve in extend templates too. Extend templates are one level only: an extend template may not itself extend:, and allez refuses a nested extend: at load.

Extend templates live inside the sub-folder

An extend template is rewritten in place for the publish, so every extend: path must point inside the sub-folder. A path that leaves the sub-folder, or an extend template that doesn't exist, is a load-time error.

External connections must be shared with the deploying SPN

A data connection's id sits inside the item definition and fab deploy publishes it unchanged, so the connection must already exist, be shared with the deploying service principal, and have a literal id per environment in a rule. Share it under Manage connections and gateways the connection Manage users, as User. Use a find_key rule rather than find_value: the committed id changes with every branch that commits, and a key-based rule finds it whatever it is.