Workflow Examples
Curated examples that progress from simple to advanced. Each example demonstrates specific workflow features and targets realistic network automation scenarios.
All example files live in the examples/ directory. They are schema-validated in CI.
Documentation markers
The example files contain # --8<-- [start:...] / # --8<-- [end:...] comments.
These are snippet markers used by the documentation system to include specific sections.
They are valid YAML comments and do not affect the workflow definition.
Runnable vs. illustrative examples
The Hello World and Show Version examples use function blocks with real Python
implementations in the Worker SDK (examples/getting-started/).
The Config Backup example uses ping and configBackup which also have SDK implementations
(examples/ping/,
examples/use-cases/config_backup/).
The remaining intermediate and advanced examples use illustrative function block names to
demonstrate workflow patterns. To run these workflows, implement the referenced function blocks
using the Worker SDK.
Error handling in production workflows
Several examples use continueOnError: true to skip failing steps gracefully. In production
workflows, always consider your error handling strategy: continueOnError is useful for
best-effort operations (e.g., reachability checks), but critical steps should fail the workflow
so operators are alerted. See Conditions and Assertions
for the full error handling toolkit.
Beginner
Hello World
The simplest possible workflow -- one function block, one step:
# A minimal workflow that runs a single function block.
# Uses seedEntity: global so it works without CMS -- ideal for getting started.
label: hello_world
name: hello_world
package: wf.example.neops.io
majorVersion: 1
minorVersion: 0
patchVersion: 0
seedEntity: global
type: workflow
steps:
- type: functionBlock
label: echo
functionBlock: "fb.examples.neops.io/echo:1.0.0"
parameters:
message: "Hello from the neops workflow engine!"
Concepts: seedEntity: global, steps, function block reference, parameter passing.
Show Version
Collect software version from all devices -- a pure (read-only) workflow:
# Collects version information from all devices and stores it as a fact.
# Demonstrates a pure (read-only) workflow -- the simplest device-scoped example.
label: show_version
name: show_version
package: wf.example.neops.io
majorVersion: 1
minorVersion: 0
patchVersion: 0
seedEntity: device
type: workflow
description: "Collect software version from all devices in scope"
steps:
- type: functionBlock
label: get_version
functionBlock: "fb.examples.neops.io/show_version:1.0.0"
runOn: device
Concepts: seedEntity: device, runOn, description, pure function block reference.
Intermediate
Config Backup with Reachability Check
Back up running config, but skip unreachable devices:
# Backs up running configuration from devices, skipping devices
# that are not reachable. Demonstrates conditions and JMESPath.
label: config_backup
name: config_backup
package: wf.example.neops.io
majorVersion: 1
minorVersion: 0
patchVersion: 0
seedEntity: device
type: workflow
description: "Backup running config from reachable devices"
steps:
- type: functionBlock
label: check_reachability
functionBlock: "fb.examples.neops.io/ping:1.0.0"
runOn: device
parameters:
host: "{{ context.device.ip }}"
continueOnError: true
- type: functionBlock
label: backup_config
functionBlock: "fb.examples.neops.io/configBackup:1.0.0"
runOn: device
condition:
type: jmes
jmes: "{{ check_reachability.result.success == true }}"
description: "Only back up reachable devices"
parameters:
backup_type: "running"
Concepts: continueOnError, conditions (condition.jmes), referencing previous step results ({{ check_reachability.result.success }}).
Interface Management
Enable interfaces based on CDP neighbor checks. Demonstrates acquire, assertions, retry, and multi-entity execution:
# Enables or disables interfaces based on CDP neighbor checks.
# Demonstrates acquire, conditions, assertions, and multi-step workflows.
label: interface_management
name: interface_management
package: wf.example.neops.io
majorVersion: 1
minorVersion: 0
patchVersion: 0
seedEntity: device
type: workflow
description: "Enable/disable interfaces based on CDP neighbor state"
acquire:
- type: expansion
entity: interface
expandFromSeedEntity: true
description: "Load all interfaces for each device"
steps:
- type: functionBlock
label: show_cdp
functionBlock: "fb.examples.neops.io/deviceShow:1.0.0"
runOn: device
parameters:
command: "show cdp neighbors"
- type: functionBlock
label: check_neighbors
functionBlock: "fb.examples.neops.io/genericJmesCheck:1.0.0"
runOn: device
parameters:
expression: "length(show_cdp.result.data.output) > `0`"
assert:
- type: jmes
jmes: "{{ show_cdp.result.success == true }}"
description: "CDP show command must have succeeded"
- type: functionBlock
label: configure_interface
functionBlock: "fb.examples.neops.io/deviceConfiguration:1.0.0"
runOn: interface
condition:
type: jmes
jmes: "{{ context.interface.state == 'DOWN' }}"
description: "Only configure interfaces that are down"
parameters:
template: "interface {{ context.interface.name }}\n no shutdown"
retryConfig:
maxRetries: 2
delay: 10
Concepts: acquire (expansion), assert, condition on interface-level steps, retryConfig, runOn: interface.
Advanced
NetBox Synchronization
Sync device metadata from NetBox into the CMS. Demonstrates elastic queries and embedded workflows:
# Synchronizes device data from NetBox into the neops CMS.
# Demonstrates elastic acquire queries, embedded workflows,
# and multi-step data pipelines.
label: netbox_sync
name: netbox_sync
package: wf.example.neops.io
majorVersion: 1
minorVersion: 0
patchVersion: 0
seedEntity: device
type: workflow
description: "Sync device metadata from NetBox to neops CMS"
acquire:
- type: elastic
entity: device
query: "facts.netbox_id: *"
assertNotEmpty: true
description: "Select devices that have a NetBox ID"
steps:
- type: functionBlock
label: fetch_netbox
functionBlock: "fb.examples.neops.io/netboxFetch:1.0.0"
runOn: device
parameters:
netbox_id: "{{ context.device.facts.netbox_id }}"
- type: workflow
label: update_metadata
name: update_metadata
steps:
- type: functionBlock
label: update_groups
functionBlock: "fb.examples.neops.io/netboxGroupUpdate:1.0.0"
runOn: device
parameters:
site: "{{ fetch_netbox.result.data.site }}"
role: "{{ fetch_netbox.result.data.role }}"
- type: functionBlock
label: update_facts
functionBlock: "fb.examples.neops.io/netboxFactsUpdate:1.0.0"
runOn: device
parameters:
netbox_data: "{{ fetch_netbox.result.data }}"
Concepts: Elastic acquire queries, assertNotEmpty, embedded workflow step, multi-step data pipeline.
NAC Rollout
Roll out 802.1X Network Access Control to switch access ports. Demonstrates a complete multi-phase workflow with pre-checks, conditional configuration, and post-verification:
# Rolls out 802.1X NAC configuration to switch interfaces.
# Demonstrates embedded workflows, Jinja templating,
# conditional success/failure paths, and retry/rollback patterns.
label: nac_rollout
name: nac_rollout
package: wf.example.neops.io
majorVersion: 1
minorVersion: 0
patchVersion: 0
seedEntity: group
type: workflow
description: "Roll out 802.1X NAC to switch access ports"
acquire:
- type: expansion
entity: device
expandFromSeedEntity: true
- type: expansion
entity: interface
expandFromSeedEntity: true
steps:
- type: workflow
label: pre_checks
name: pre_checks
steps:
- type: functionBlock
label: verify_connectivity
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
parameters:
commands:
- "show dot1x all"
- "show authentication sessions"
- type: functionBlock
label: apply_nac_config
functionBlock: "fb.examples.neops.io/configureDevice:1.0.0"
runOn: interface
condition:
type: jmes
jmes: "{{ context.interface.facts.port_type == 'access' }}"
description: "Only configure access ports"
parameters:
template: |
interface {{ context.interface.name }}
authentication port-control auto
dot1x pae authenticator
retryConfig:
maxRetries: 3
delay: 30
- type: functionBlock
label: verify_nac
functionBlock: "fb.examples.neops.io/genericJmesCheck:1.0.0"
runOn: device
parameters:
expression: "length(collect_facts.result.data.output) > `0`"
Concepts: Group-level seedEntity, multi-level acquire (device + interface expansion), embedded workflow for pre-checks, Jinja templating in parameters, interface-level conditions, retry configuration, post-deployment verification.
Getting and Using the Examples
The example workflow files live in the examples/ directory of the workflow engine repository.
Clone the repository:
Deploy an example to the engine:
yq -o=json hello-world.workflow.yaml | \
jq '{workflow: .}' | \
curl -X POST http://localhost:3030/workflow-definition \
-H "Content-Type: application/json" -d @-
Run the matching function blocks: The echo and show_version function blocks have real implementations in the Worker SDK's examples/getting-started/ directory. The echo FB is the simplest possible function block -- it returns its input parameters as results, making it ideal for verifying your setup works end-to-end. Start a worker with:
cd /path/to/neops-worker-sdk-py
URL_BLACKBOARD=http://localhost:3030 DIR_FUNCTION_BLOCKS=examples/getting-started uv run neops_worker
Validating Your Workflows
VS Code Schema Association
Add this to your VS Code settings.json to get autocompletion and validation for workflow YAML files:
Command-Line Validation
Validate a workflow file against the schema:
npx ajv validate -s schema/RootWorkflow.schema.json -d my-workflow.workflow.yaml --spec=draft7 -c ajv-formats --strict=false
Or use the Makefile target: