Skip to content

Steps

Steps are the building blocks of a workflow. Each step is one of three types: a function block invocation, an inline (embedded) workflow, or a reference to another workflow definition.

Function Block Step

The most common step type. It executes a registered function block:

- type: functionBlock
  label: get_version
  functionBlock: "fb.examples.neops.io/show_version:1.0.0"
  runOn: device
  parameters:
    timeout: "{{ parameters.timeout }}"
Field Required Description
type Yes Always functionBlock
label Yes Unique identifier within the workflow (alphanumeric + underscore)
functionBlock Yes SemVer reference (package/name:version)
runOn No Entity type to execute on (overrides FB default)
parameters No Key-value pairs passed to the FB (supports JMESPath interpolation)
rawParameters No Literal parameters (not interpolated)
condition No Skip this step if condition evaluates to false
assert No Fail if any assertion evaluates to false
acquire No Acquire additional entities for this step
retryConfig No Retry on failure (schema-defined, not yet enforced)
repeatConfig No Repeat the step multiple times (schema-defined, not yet enforced)
continueOnError No Continue workflow on failure (default: false) (schema-defined, not yet enforced)
delay No Wait N seconds before executing (0-600)
description No Human-readable description

The label is how subsequent steps reference this step's results: {{ get_version.result.data.version }}.

Embedded Workflow Step

Group multiple steps into a logical unit. Useful for organizing complex workflows into phases:

- type: workflow
  label: pre_checks
  name: pre_checks
  steps:
    - type: functionBlock
      label: ping
      functionBlock: "fb.examples.neops.io/ping:1.0.0"
      runOn: device
      parameters:
        host: "{{ context.device.ip }}"

    - type: functionBlock
      label: collect_facts
      functionBlock: "fb.examples.neops.io/facts:1.0.0"
      runOn: device

Embedded workflows have the same fields as the root workflow (steps, name, config, acquire, etc.) plus the step-level fields (label, condition, assert, continueOnError).

An embedded workflow:

  • Executes its steps in sequence (or parallel if config.executionStrategy.parallel: true)
  • Shares the parent workflow's execution context
  • Can have its own acquire clause to pull in additional entities
  • Is part of the parent workflow's transaction -- it does not create a separate execution

Workflow Reference Step

Not Yet Implemented

Workflow references are defined in the schema and documented here for planned behavior, but the execution engine does not yet support them. Attempting to execute a workflow containing a workflowReference step will result in a runtime error. Use embedded workflow steps for composition in the meantime.

Reference another workflow definition. This enables reuse across workflows:

- type: workflowReference
  label: backup
  workflow: "wf.neops.io/config_backup:1.0.0"
  strategy: embed
  parameters:
    backup_type: "running"
Field Required Description
type Yes Always workflowReference
label Yes Unique identifier
workflow Yes SemVer reference to the target workflow definition
strategy Yes embed or dispatch
parameters No Parameters to pass to the referenced workflow (JMESPath interpolated)
rawParameters No Literal parameters (not interpolated, takes precedence on key conflicts)

Embed vs Dispatch (Planned)

embed -- The referenced workflow's steps are inlined into the current execution. They share the same context and transaction. This is equivalent to copy-pasting the steps.

dispatch -- The referenced workflow runs as a separate execution with its own context and transaction. The parent workflow waits for it to complete. Use this for independent sub-workflows that should have their own locking and failure isolation.

graph TD
    subgraph embed ["embed — same context & transaction"]
        direction TB
        E1["Step 1"] --> EA["Ref Step A<br/><i>inlined</i>"]
        EA --> EB["Ref Step B<br/><i>inlined</i>"]
        EB --> E3["Step 3"]
    end

    subgraph dispatch ["dispatch — separate execution"]
        direction TB
        D1["Step 1"] --> DR["Workflow Ref"]
        DR -. "launches & waits" .-> DS["Sub-Workflow<br/>(own context)"]
        DS -. "completes" .-> D3["Step 3"]
    end

Step Execution Order

Steps execute in the order they appear in the steps array. Within each step, the engine creates jobs for all entities in scope (determined by runOn). By default, all jobs for a step must complete before the next step begins.

With config.executionStrategy.parallel: true on the workflow, the engine allows per-entity pipelining: device 1 can proceed to step 2 while device 2 is still on step 1.

Choosing the Right Step Type

Use case Step type
Run a single function block functionBlock
Group related steps (e.g., "pre-checks", "configure", "verify") workflow (embedded)
Reuse a workflow across multiple parent workflows workflowReference with embed (planned)
Run a sub-workflow with independent failure isolation workflowReference with dispatch (planned)

See also: