Skip to content

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):

allez deploy fabric --env test --plan

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, as allez-orchestration does above: its config.yml opens 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 its environments: 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_scope lists 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 a publish filter 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:

  1. Is anything being deleted? Every - line that says will be deleted, in every node; a lakehouse, warehouse or SQL database takes its data with it (found by name).
  2. Does every new environment-specific id have a rule? A + or ~ item that references another workspace, a connection or an id needs a parameter.yml rule; allez audit fails on any that lacks one.
  3. 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.
  4. Is the order right? The tree. A node that reads another's item must sit under it.