You can pass data between steps using input and output parameters, but it’s not just about moving values; it’s about creating a directed graph of computation where each step’s output becomes another’s input.

Let’s see this in action with a simple workflow that takes a user’s name and age, processes them, and then prints a greeting.

# workflow.yaml
version: 1.0

steps:
  - name: get_user_info
    type: command
    command: echo "name=Alice age=30"
    outputs:
      - name: user_name
        type: string
      - name: user_age
        type: integer

  - name: process_user
    type: command
    command: echo "processed_name=${steps.get_user_info.outputs.user_name} processed_age=$((steps.get_user_info.outputs.user_age * 2))"
    inputs:
      - name: user_name
        from: steps.get_user_info.outputs.user_name
      - name: user_age
        from: steps.get_user_info.outputs.user_age
    outputs:
      - name: processed_name
        type: string
      - name: processed_age
        type: integer

  - name: greet_user
    type: command
    command: echo "Hello, ${steps.process_user.outputs.processed_name}! Your doubled age is ${steps.process_user.outputs.processed_age}."
    inputs:
      - name: processed_name
        from: steps.process_user.outputs.processed_name
      - name: processed_age
        from: steps.process_user.outputs.processed_age

When this workflow runs, the get_user_info step executes first. Its command outputs name=Alice age=30. The workflow parser then extracts these key-value pairs and assigns them to the user_name and user_age output parameters.

The process_user step is configured to depend on get_user_info because it references its outputs in its inputs section. The from directive tells the system exactly where to get the data: steps.get_user_info.outputs.user_name and steps.get_user_info.outputs.user_age. Inside the process_user step’s command, these inputs are used. The echo command constructs a string processed_name=Alice processed_age=60. These are then captured by the process_user step’s own outputs.

Finally, the greet_user step, depending on process_user, takes the processed_name and processed_age as inputs and uses them to form the final greeting: "Hello, Alice! Your doubled age is 60."

The system doesn’t just magically know how to map steps.get_user_info.outputs.user_name to the user_name input of process_user. The from directive is crucial. It’s the explicit pointer that establishes the data dependency. Without it, the input parameter would remain empty. The type annotations (type: string, type: integer) are also important; the system can perform basic type coercion if necessary, preventing runtime errors from mismatched data types. You can also pass complex data structures like JSON objects, which are then accessible as nested properties within subsequent steps.

The true power here is in how this forms a dependency graph. The system automatically determines the execution order based on these from directives. If process_user also needed user_name from get_user_info, you’d simply add another input to process_user with from: steps.get_user_info.outputs.user_name. This explicit declaration of data flow is what enables complex, multi-step processes to be defined and executed reliably.

You’ll next want to explore how to handle conditional execution based on the values of these parameters.

Want structured learning?

Take the full Argo-workflows course →