Reading the plan¶
allez deploy --plan prints what a deploy would do without doing it. It is what an approver reads before a deploy. In the example pipelines the PlanTest and PlanProd jobs run it for the next environment and publish it as the plan-test and plan-prod artifacts, read before the fabric-test and fabric-prod approvals. It makes no fab call, so it needs no credentials, and anyone can run it on a laptop against any environment. The same facts as JSON, for tools, come from allez document.
Below is the sample solution's plan for test. Run with --since, each node also gets a changes section listing what the deploy will change (below):
How --since works
--since <git ref> names the commit the target environment holds. For each node, allez asks git which item folders in the node's scope differ between that commit and the working tree, and prints the answer as the changes section. Git alone answers it.
The example pipeline's fab-deploy-steps.yml template, and the allez repo's fab-deploy action, have a separate record the deploy step. After a successful deploy to a named environment, it creates or moves deployed/<env> to the deployed commit and pushes the tag. The next plan uses that tag with --since deployed/<env> (deployment tags and permissions).
allez itself never writes this tag. If you use your own pipeline, record the successfully deployed commit there too, or pass another existing git ref. Before the first deploy there is no tag, so the example pipeline runs without --since. Passing a missing tag directly to allez fails.
On a laptop, any ref works: --since main, --since v1.3.0, --since deployed/prod after a git fetch --tags. The diff is against your working tree, so an unsaved edit counts. A ref git cannot resolve fails before anything else runs.
Plan¶
dag (--env test; numbers are deploy order)
└── 1 ● allez-orchestration/config.yml
├── post_deploy_hooks: allez-orchestration/.deploy/hooks/schema.hooks.yml
└── 2 ● allez-data/config.yml
├── post_deploy_hooks: allez-data/.deploy/hooks/schema.hooks.yml, allez-data/.deploy/hooks/ingest.hooks.yml
└── 3 ● allez-reporting/config.yml
├── pre_deploy_hooks: allez-reporting/.deploy/hooks/takeover.hooks.yml
├── post_deploy_hooks: allez-reporting/.deploy/hooks/refresh.hooks.yml
└── 4 ● allez-orchestration/.deploy/config-overlay/pipeline.config.yml
└── post_deploy_hooks: allez-orchestration/.deploy/hooks/run.hooks.yml
─────────────────────────────────── 1 ● allez-orchestration ───────────────────────────────────
PLAN allez deploy
config
Folder: /home/vsts/work/1/s/fabric/allez-orchestration
Config: /home/vsts/work/1/s/fabric/allez-orchestration/config.yml
Env: test
Target: workspace=allez-orchestration-test
Item types: SQLDatabase
Parameters: parameter.yml
Publish: enabled
Unpublish: enabled
parameters
· deferred $allez.allez-data.$items.Notebook.Notebook.$id: its rule is for DataPipeline, which this node does not publish
· deferred $allez.allez-data.$workspace.$id: its rule is for DataPipeline, which this node does not publish
· deferred $allez.allez-reporting.$items.SemanticModel.SemanticModel.$id: its rule is for DataPipeline, which this node does not publish
· deferred $allez.allez-reporting.$workspace.$id: its rule is for DataPipeline, which this node does not publish
fab deploy
Would run: fab deploy --config /home/vsts/work/1/s/fabric/allez-orchestration/config.yml --target_env test --force
post_deploy_hooks (1 step(s) from schema.hooks.yml)
Would run publish_sqlproj (library) item=Control.SQLDatabase timeout_seconds=900
──────────────────────────────────────── 2 ● allez-data ────────────────────────────────────────
PLAN allez deploy
config
Folder: /home/vsts/work/1/s/fabric/allez-data
Config: /home/vsts/work/1/s/fabric/allez-data/config.yml
Env: test
Target: workspace=allez-data-test
Item types: Lakehouse, Environment, Notebook, Warehouse, DataBuildToolJob
Parameters: parameter.yml
Publish: enabled
Unpublish: enabled
Excluded: Scratch.Notebook (publish.exclude_regex)
parameters
· parameter.yml, dbt.yml: no $allez tokens
fab deploy
Would run: fab deploy --config /home/vsts/work/1/s/fabric/allez-data/config.yml --target_env test --force
post_deploy_hooks (1 step(s) from schema.hooks.yml)
Would run publish_sqlproj (library) item=Warehouse.Warehouse timeout_seconds=900
post_deploy_hooks (1 step(s) from ingest.hooks.yml)
Would run run_notebook (library) item=Notebook.Notebook timeout_seconds=900 parameters={seed_rows: 1000}
───────────────────────────────────── 3 ● allez-reporting ─────────────────────────────────────
PLAN allez deploy
config
Folder: /home/vsts/work/1/s/fabric/allez-reporting
Config: /home/vsts/work/1/s/fabric/allez-reporting/config.yml
Env: test
Target: workspace=allez-reporting-test
Item types: Report, SemanticModel
Parameters: parameter.yml
Publish: enabled
Unpublish: enabled
pre_deploy_hooks (1 step(s) from takeover.hooks.yml)
Would run fab_api (library) audience=powerbi method=POST endpoint=groups/<workspace id of allez-reporting-test>/datasets/<SemanticModel.SemanticModel.id>/Default.TakeOver
parameters
✓ would resolve $allez.allez-data.$items.Warehouse.Warehouse.$id → <id of Warehouse.Warehouse in 'allez-data'>
✓ would resolve $allez.allez-data.$workspace.$id → <workspace id of allez-data-test>
fab deploy
Would run: fab deploy --config /home/vsts/work/1/s/fabric/allez-reporting/config.yml --target_env test --force
post_deploy_hooks (3 step(s) from refresh.hooks.yml)
Would run refresh_semanticmodel (library) item=SemanticModel.SemanticModel timeout_seconds=900 request={type: full, commitMode: transactional}
Would run fab_api (library) method=GET endpoint=workspaces/<workspace id of allez-reporting-test>/reports
· Schedule the nightly refresh — skipped (environments ['prod'])
──────────────────────── 4 ● allez-orchestration [pipeline.config.yml] ────────────────────────
PLAN allez deploy
config
Folder: /home/vsts/work/1/s/fabric/allez-orchestration
Config: /home/vsts/work/1/s/fabric/allez-orchestration/config.yml
Overlay: allez-orchestration/.deploy/config-overlay/pipeline.config.yml (merged as --params)
Env: test
Target: workspace=allez-orchestration-test
Item types: DataPipeline (overlay)
Parameters: parameter.yml
Publish: enabled
Unpublish: enabled
parameters
✓ would resolve $allez.allez-data.$items.Notebook.Notebook.$id → <id of Notebook.Notebook in 'allez-data'>
✓ would resolve $allez.allez-data.$workspace.$id → <workspace id of allez-data-test>
✓ would resolve $allez.allez-reporting.$items.SemanticModel.SemanticModel.$id → <id of SemanticModel.SemanticModel in 'allez-reporting'>
✓ would resolve $allez.allez-reporting.$workspace.$id → <workspace id of allez-reporting-test>
fab deploy
Would run: fab deploy --config /home/vsts/work/1/s/fabric/allez-orchestration/config.yml --target_env test --force --params <pipeline.config.yml>
post_deploy_hooks (1 step(s) from run.hooks.yml)
Would run run_pipeline (library) item=Orchestrate.DataPipeline timeout_seconds=1800
On a laptop each node is separated by a rule, as above. On an Azure Pipelines agent each node is a collapsible group with the same title, and each phase inside it (config, changes, parameters, fab deploy, pre_deploy_hooks, post_deploy_hooks) is a bold line. The log on screen is coloured; the published artifact has the colour stripped and reads exactly like this. Green and red are kept for the changes lines, and the dag tree's nodes are bold cyan like the fold titles they open.
DAG¶
dag (--env test; numbers are deploy order)
└── 1 ● allez-orchestration/config.yml
├── post_deploy_hooks: allez-orchestration/.deploy/hooks/schema.hooks.yml
└── 2 ● allez-data/config.yml
The first thing printed is the dag as a tree. Roots depend on nothing. Every other node hangs under the node it depends on, and the number in front is its place in deploy order. Hooks files hang off their node under the key that names them.
- A node with several dependencies is drawn in full under the last one to publish, and as
→ n <config> (also depends on this)under the others. A workspace the dag revisits reads as a chain, asallez-orchestrationdoes above: itsconfig.ymlopens the run and its overlay closes it. - A node outside this run is dimmed with the reason,
(outside this run)when you deploy one sub-folder, or(not for --env test)when itsenvironments:filter excludes the env. - Before the tree, a line names any
config.yml, overlay or hooks file on disk that no node names:not in the dag, ignored: …. Nothing deploys it. If you expected it to, add it to the dag.
One node¶
Each node then prints its own block, in deploy order. The title is the node's number and sub-folder, so it matches the tree by eye.
config¶
config
Folder: /home/vsts/work/1/s/fabric/allez-data
Config: /home/vsts/work/1/s/fabric/allez-data/config.yml
Env: test
Target: workspace=allez-data-test
Item types: Lakehouse, Environment, Notebook, Warehouse, DataBuildToolJob
Parameters: parameter.yml
Publish: enabled
Unpublish: enabled
Excluded: Scratch.Notebook (publish.exclude_regex)
What fab deploy will apply, read from the node's config.yml for --env. A setting written as a mapping by environment (item_types_in_scope: {dev: [Notebook, Lakehouse], prod: [Notebook]}) prints its entry for --env, the way fabric-cicd reads it (per-environment values):
| Line | Meaning |
|---|---|
Target |
The workspace this node publishes into, from core.workspace.<env>; with --branch-workspace, the branch workspace. |
Item types |
core.item_types_in_scope, resolved for --env: the only item types this node publishes. An item folder of another type is not touched by this node, and not listed under changes or Excluded either. |
Parameters |
The parameter file the config names, if any. |
Publish, Unpublish |
Whether this env publishes, and whether it deletes orphans: items in the workspace that the repo no longer has. Unpublish: enabled on prod is the line to read twice. |
Excluded |
Printed only when a publish filter other than the type scope excludes an item: each item the node will not publish, with the key that excludes it, in the item's folder when it sits inside one. A folder filter reads Excluded: DEBUG_Notes.Notebook (publish.exclude_regex), legacy/Old.Report (publish.folder_exclude_regex). The key named is the first filter, in fabric-cicd's order, that excludes the item. In the sample, allez-data keeps a scratch notebook committed for reference and excludes it with publish.exclude_regex: "^Scratch". |
Overlay |
Shown for an overlay node: the overlay file, merged into the lines above the way fabric-cicd merges --params. A line the overlay file set reads (overlay). Any other setting the overlay file sets gets a line of its own. |
A filter fabric-cicd would refuse (one set without its features: flag, both folder filters for one environment, a regex that does not compile) fails the plan at load, naming the file, the key and the fix, before any block prints.
changes¶
changes
since ea4a7ad (assumes test holds that commit)
+ Orders.Notebook new
~ SemanticModel.SemanticModel changed: definition/expressions.tmdl, definition/tables/Sales.tmdl
~ Ingest.Notebook changed, ids only: notebook-content.py
- Legacy.Report removed; unpublish enabled, will be deleted
- Scratch.Lakehouse removed; unpublish skipped (kept)
- DEBUG_Notes.Notebook removed; excluded by unpublish.exclude_regex (kept)
- Old.Report removed; not in unpublish.items_to_include (kept)
= 4 unchanged
deploy config changed: parameter.yml, .deploy/hooks/ingest.hooks.yml
Printed only with --since <git ref>. It lists the item folders the node publishes that differ between that commit and the working tree, one line each, from git alone:
| Marker | Meaning |
|---|---|
+ |
A folder the base commit did not have. The deploy creates it. |
~ |
A folder whose files changed. The changed files are named, relative to the item folder. The deploy updates the item. |
~ … ids only |
Dimmed. Every changed file is the same as before once its GUIDs are ignored. This is usually allez deploy --branch-workspace --commit-to-git committing back the ids its branch workspace gave the item, merged in with a feature. It says only ids moved, not that the change is harmless: a notebook pointed at a different lakehouse is also ids only. |
- |
A folder the base commit had and the working tree does not. The item is an orphan, and the rest of the line is what fabric-cicd will do with it, from this node's unpublish config: deleted, or kept because unpublish.skip is on for the env, its name matches unpublish.exclude_regex, or unpublish.items_to_include is set and does not list it (unpublish rules). |
= |
How many item folders in scope did not change. When nothing changed the line reads = all <n> items unchanged and nothing else is listed. |
deploy config changed: |
Changed files in the sub-folder outside item folders: config.yml, parameter.yml, an overlay, a hooks file, a template parameter.yml extends. These change what deploys even when no item did. |
- A rename is two lines, a
-and a+. fabric-cicd matches items by name and type, so a renamed item is a new item and the old one is an orphan (found by name). - The header states the assumption: the list is right if the environment holds the base commit. A base that is not an ancestor of the current commit is still diffed, and the header adds
; <base> is not an ancestor of HEAD. - The scope is the node's. An overlay node that narrows
item_types_in_scopelists only those types, so a workspace two dag nodes deploy to can list the same item under each, which is what will happen. An item apublishfilter excludes is not listed at all, even when its files changed: the deploy will not touch it.
pre_deploy_hooks and post_deploy_hooks¶
post_deploy_hooks (1 step(s) from ingest.hooks.yml)
Would run run_notebook (library) item=Notebook.Notebook timeout_seconds=900 parameters={seed_rows: 1000}
· Schedule the nightly refresh — skipped (environments ['prod'])
One header per hooks file the dag node names, in the node's order, before or after the publish as the key says. Each step is one line: the hook it runs, where allez found it ((library) for a hook that ships with allez, (.allez/hooks) for the solution's own), and the keys it will be called with, resolved for this env. A step whose environments: filter excludes this env prints as skipped, with the filter. A hook that does not exist, or a step with a key the hook does not take, fails the plan before this line is reached (hooks files).
parameters¶
parameters
✓ would resolve $allez.allez-data.$items.Warehouse.Warehouse.$id → <id of Warehouse.Warehouse in 'allez-data'>
✓ would resolve $allez.allez-data.$workspace.$id → <workspace id of allez-data-test>
Every $allez.* token in the node's parameter.yml and the templates it extends, and what each would resolve to ($allez.* variables). An id the plan has not cached prints as a placeholder in angle brackets; a live run looks it up. A file with no tokens says so, and a node with no parameter file says so. A token that names a sub-folder or item the solution does not have fails the plan here.
A token carried only by rules for item types this node does not publish prints dimmed as · deferred …, with the type its rule is for. It is left as written and resolves under the node that publishes that type; in the sample, the pipeline's four tokens are deferred at node 1 and resolved at node 4 (Overlays).
fab deploy¶
fab deploy
Would run: fab deploy --config /home/vsts/work/1/s/fabric/allez-data/config.yml --target_env test --force
The exact command a live run makes. An overlay node adds --params <overlay name>; a branch-workspace deploy adds the retarget the same way. This is the one line a prod approver is approving, and it never wraps.
git¶
Printed only by allez deploy --branch-workspace --commit-to-git, after a workspace's last node: what it would connect, initialise and commit (what a run does).
Before you approve¶
The plan answers four questions. Read them in this order:
- Is anything being deleted? Every
-line that sayswill be deleted, in every node; a lakehouse, warehouse or SQL database takes its data with it (found by name). - Does every new environment-specific id have a rule? A
+or~item that references another workspace, a connection or an id needs aparameter.ymlrule;allez auditfails on any that lacks one. - Are the hook steps the ones you expect, for this env? A notebook run or a model refresh on prod is a hook line here first.
- Is the order right? The tree. A node that reads another's item must sit under it.