Matrix examples
Here are some examples of matrix strategies you can use in your stages or steps. For information about how matrix strategies work and instructions for creating different types of looping strategies, go to Use looping strategies.
Loop versus matrix
A matrix is a pattern that iterates over a series of elements. You might know this as a loop or for loop.
These examples compare how you would write a loop to iterate over a series of elements versus how you would use a matrix to achieve the same iterating "for each" effect.
Basic loop
With a basic loop, you provide a list of elements and iterate over the list. In a Harness pipeline, the iterations could be individual steps or entire stages.
For example, assume you want to repeat an entire stage for each item in a list.
As a for loop, you might write something like this:
listOfString=["a", "b", "c"]
for(String s: listOfString) {
	//run the stage
}
As a matrix in a Harness pipeline, you would write:
stage:
 matrix:
   listOfString: ["a", "b", "c"]
In your steps, you use expressions to indicate where you want to insert the matrix values. For example, the above example would be referenced by <+matrix.listOfString>.
Nested loops
Just as you can write nested for loops, you can create nested matrices in Harness.
Here's an example of nested for loops in Python:
for(String s: listA) {
	for(String s1: listB) {
	  stdout(s) // it would print the value of element being iterated on in listA
	  stdout(s1) // it would print the value of element being iterated on in listB
	}
}
To create nested, multi-dimensional matrices in Harness, you add another list of values to iterate over.
For example, this matrix iterates over each item in listB for each item in listA:
stage:
 matrix:
   listA: ["a", "b", "c"]
   listB: ["some", "random", "list"]
This matrix produces a total of 9 iterations:
- a+- some,- a+- random,- a+- list
- b+- some,- b+- random,- b+- list
- c+- some,- c+- random,- c+- list
Referencing values from nested matrices is the same as with one-dimensional matrices. In your steps, you use expressions to indicate where you want to insert the matrix values. For example, in the above example, to refer to the elements from listA, you would use the expression <+matrix.listA>, and to refer to elements from listB, you would use the expression <+matrix.listB>.
Basic matrix: Repeat over a list
The simplest form of a matrix strategy is one-dimensional. It includes a single list of values to iterate over.
matrix:
  TAG: [ "value1", "value2", "value3" ] ## Supply a user-specified tag and a comma-separated list of values.
  maxConcurrency: 3 ## Optional but recommended. Specify the maximum number of instances that can run concurrently.
This example loops over a series of JDK versions.
matrix:
  jdk: [ "18", "17", "16", "15", "14", "13", "12", "11", "10", "9" ]
  maxConcurrency: 2
A one-dimensional matrix is the only form of matrix strategy that you can also define in a repeat strategy. For example:
repeat:
  items: [ "18", "17", "16", "15", "14", "13", "12", "11", "10", "9" ] ## 'items' is required. Provide a comma-separated list of values.
  maxConcurrency: 2 ## Optional but recommended. Specify the maximum number of instances that can run concurrently.
Use fixed values for matrix values
The following examples use fixed values for the matrix strategy dimensions.
Run an app on multiple browsers and operating systems
Suppose you have a pipeline that builds a Go app, and you want to test the app on three different platforms and three different browsers. In the stage or step where you test the app, you can define a matrix strategy that loops over your desired browser and os values, for example:
matrix:
  browser: [chrome, safari, firefox]
  os: [macos, windows, linux]
  maxConcurrency: 3
The browser and os are the dimensions of your matrix. When you write matrix strategies in Harness, each matrix dimension consists of a user-specified tag with a comma-separated list of values.
Use maxConcurrency to specify the maximum number of instances that can run at once. This example matrix has nine combinations. Rather than overload the build infrastructure with nine concurrent runs, setting maxConcurrency to 3 limits the pipeline to three concurrent runs. The remaining combinations are queued while the first three run.
Deploy multiple services to multiple environments
You can use a matrix strategy on a Deploy stage to deploy multiple services to multiple environments. The following example includes dimensions to deploy three services on two environments. It uses exclude to ignore one combination, and it sets the maxConcurrency to 2.
matrix:
  service: [svc1, svc2, svc3] ## First dimension.
  environment: [env1, env2] ## Second dimension.
  exclude: ## Specify combinations to ignore or skip.
    - service: svc1
      environment: env1
  maxConcurrency: 2 ## Optional but recommended. Specify the maximum number of instances that can run concurrently.
The service and environment are the dimensions of the matrix. Each matrix dimension consists of a user-specified tag with a comma-separated list of values.
In a pipeline where you used the above matrix strategy, you could use the expression <+matrix.service> on the Service tab of the Deploy stage to call the service values.

Use runtime input for matrix values
You can use runtime input (<+input>) for values and entire matrix strategies.
When you run the pipeline, you are prompted to provide the values (which you can provide as an array) or the entire strategy definition, depending on how you specified the runtime input.
Use runtime input for one dimension
To use runtime input for the values in a matrix dimension, enter <+input> in your pipeline YAML:
matrix:
  TAG: <+input>
For example, the matrix strategy used in the following pipeline would prompt you to provide a list of values for goVersion at pipeline runtime:
pipeline:
  name: MatrixAxisAsRuntimeInput
  identifier: MatrixAxisAsRuntimeInput
  tags: {}
  stages:
    - stage:
        name: exampleStage
        identifier: exampleStage
        description: ""
        type: Custom
        spec:
          execution:
            steps:
              - step:
                  type: ShellScript
                  name: Shell Script_1
                  identifier: ShellScript_1
                  spec:
                    shell: Bash
                    onDelegate: true
                    source:
                      type: Inline
                      spec:
                        script: echo "Hello world!"
                    environmentVariables: []
                    outputVariables: []
                  timeout: 10m
                  failureStrategies: []
                  strategy:
                    matrix:
                      goVersion: <+input>
Here is an example of a possible input set for this pipeline:
pipeline:
  identifier: MatrixExamples
  stages:
    - stage:
        identifier: exampleStage
        type: Custom
        spec:
          execution:
            steps:
              - step:
                  identifier: ShellScript_1
                  type: ShellScript
                  strategy:
                    matrix:
                      goVersion:
                        - 1.1.0
                        - 1.1.1
Use runtime input for multiple dimensions
You can use runtime input for multiple dimensions in a matrix. You can also use a combination of runtime input and other values (such as fixed values, expressions, or complex JSON). For example, the following matrix strategy uses runtime input for two dimensions and fixed values for the third dimension.
 strategy:
    matrix:
      image: <+input>
      version: <+input>
      tags:
        - "harness"
        - "core"
      maxConcurrency: 3
The number of instances generated by this strategy depends on the number of values you provide for each runtime input. It is a good idea to include maxConcurrency with this strategy in case a lot of values are provided.
Here is a YAML example of a pipeline with the above matrix strategy:
pipeline:
  name: MatrixAxisAsRuntimeInput
  identifier: MatrixAxisAsRuntimeInput
  tags: {}
  stages:
    - stage:
        name: exampleStage
        identifier: exampleStage
        description: ""
        type: Custom
        spec:
          execution:
            steps:
              - step:
                  type: ShellScript
                  name: Shell Script_1
                  identifier: ShellScript_1
                  spec:
                    shell: Bash
                    onDelegate: true
                    source:
                      type: Inline
                      spec:
                        script: echo "Hello world!"
                    environmentVariables: []
                    outputVariables: []
                  timeout: 10m
                  failureStrategies: []
                  strategy:
                    matrix:
                      image: <+input>
                      version: <+input>
                      tags:
                       - "harness"
                       - "core"
Here is an example of a possible input set for this pipeline. Note that tags is not included because those values are fixed values.
pipeline:
  identifier: MatrixExamples
  stages:
    - stage:
        identifier: exampleStage
        type: Custom
        spec:
          execution:
            steps:
              - step:
                  identifier: ShellScript_1
                  type: ShellScript
                  strategy:
                    matrix:
                      image:
                        - nginx
                        - ingress
                      version:
                        - latest
                        - 1.2.0
Use runtime input for the entire matrix strategy
To require runtime input for an entire matrix strategy, use matrix: <+input>. You'll have to provide the entire matrix strategy when you run the pipeline.
For example, you could use this if you need to input different dimensions, values, and exclusions at runtime.
Here is an example of a pipeline that uses runtime input for the entire matrix strategy:
pipeline:
  name: MatrixAsRuntimeInput
  identifier: MatrixAsRuntimeInput
  tags: {}
  stages:
    - stage:
        name: exampleStage
        identifier: exampleStage
        description: ""
        type: Custom
        spec:
          execution:
            steps:
              - step:
                  type: ShellScript
                  name: Shell Script_1
                  identifier: ShellScript_1
                  spec:
                    shell: Bash
                    onDelegate: true
                    source:
                      type: Inline
                      spec:
                        script: echo "Hello world!, my name is <+matrix.name>"
                    environmentVariables: []
                    outputVariables: []
                  timeout: 10m
                  failureStrategies: []
                  strategy:
                    matrix: <+input>
        tags: {}
Here is an example of an input set for this pipeline:
pipeline:
  identifier: MatrixExamples
  stages:
    - stage:
        identifier: exampleStage
        type: Custom
        spec:
          execution:
            steps:
              - step:
                  identifier: ShellScript_1
                  type: ShellScript
                  strategy:
                    matrix:
                      image:
                        - nginx
                        - ingress
                      version:
                        - 10
                        - 11
                        - 12
                      exclude:
                        - image: nginx
                          version: 12
Get matrix values from expressions
You can use expressions as values for matrix dimensions. You can also use expressions to extract values from elsewhere, such as from pipeline or stage variables.
For example, the following pipeline uses a matrix strategy that loops over a series of Jira issues. The issue numbers are derived from the pipeline variable jiraTickets, which is populated at runtime (as indicated by it's value <+input>). The matrix strategy uses an expression with the split method to separate the individual issue numbers from the pipeline variable: <+<+pipeline.variables.example>.split(',')>.
pipeline:
  name: RepeatJiraTickets
  identifier: RepeatJiraTickets
  projectIdentifier: naidusanity
  orgIdentifier: default
  tags: {}
  stages:
    - stage:
        name: approval1
        identifier: approval1
        description: ""
        type: Approval
        spec:
          execution:
            steps:
              - step:
                  type: JiraApproval
                  name: Jira Approval_1
                  identifier: JiraApproval_1
                  spec:
                    connectorRef: abc
                    issueKey: <+matrix.jiraTicket>
                    approvalCriteria:
                      type: KeyValues
                      spec:
                        matchAnyCondition: true
                        conditions:
                          - key: Status
                            operator: equals
                            value: Done
                    rejectionCriteria:
                      type: KeyValues
                      spec:
                        matchAnyCondition: true
                        conditions: []
                  timeout: 1d
                  failureStrategies: []
                  strategy:
                    matrix:
                      jiraTicket: <+<+pipeline.variables.jiraTickets>.split(',')>
        tags: {}
  variables:
    - name: jiraTickets
      type: String
      description: ""
      value: <+input>
Here is an example of a possible input set for this pipeline:
pipeline:
  identifier: RepeatJiraTickets
  variables:
    - name: jiraTickets
      type: String
      value: cd-1,cd-2,cd-3 ## Jira issue numbers provided at runtime.
Get matrix values from trigger payloads
To provide the axis value from a trigger, use codebase expressions or trigger payload expressions to define the axis values.
For example:
 strategy:
    matrix:
        payload: [<+trigger.payload.user.username>, <+trigger.prNumber>]
You can use <+trigger.payload.PATH_IN_JSON> to reference any field in the trigger's JSON payload, such as <+trigger.payload.pull_request.user.login>.
Use complex JSON for matrix values
If you are not sure about the value of an item in a pipeline, you can store it as a JSON string instead. This way, if an object is an output of the previous step, you can use the JSON functor to get a list from that object.
The following example shows a pipeline that provides a custom object using JSON functor. The matrix strategy references a pipeline variable containing the JSON object.
pipeline:
  name: matrix_split
  identifier: matrix_split
  projectIdentifier: naidusanity
  orgIdentifier: default
  tags: {}
  stages:
    - stage:
        name: s2
        identifier: s2
        description: ""
        type: Custom
        spec:
          execution:
            steps:
              - step:
                  type: ShellScript
                  name: Shell Script_1
                  identifier: ShellScript_1
                  spec:
                    shell: Bash
                    onDelegate: true
                    source:
                      type: Inline
                      spec:
                        script: |-
                          echo <+matrix.service.name>
                          echo <+matrix.service.version>
                          echo <+json.list("a", <+pipeline.variables.services>)>
                    environmentVariables: []
                    outputVariables: []
                  timeout: 10m
        tags: {}
        strategy:
          matrix:
            service: <+json.list("a", <+pipeline.variables.services>)>
  variables:
    - name: services
      type: String
      description: ""
      value: "{\"a\":[ { \"name\": \"svc1\", \"version\": \"<+pipeline.variables.version>\" }, { \"name\": \"svc2\", \"version\": \"1.0.2\" }, { \"name\": \"svc3\", \"version\": \"1.0.1\" } ]}"
    - name: version
      type: String
      value: "1.0.0"
Escaping is required for some punctuation. Note the use of double quotes around the entire object.
Also, when an expression is used in a JSON string, it must be wrapped in quotation marks, for example, <+pipeline.variables.version> in the above pipeline YAML.
Generate random integers using a matrix and then find their aggregate sum.
Solution:
Requirements:-
- Enable project setting Enable Json Support for expressions.
- Install jq library.

We are going to create a two-step process: the first step is for generating random integers, and the second step is for calculating their aggregate sum.
First step
In this step we are using built-in variable in Bash $RANDOM that generates a random integer, here it generates a random integer between 1 and 100 and stores it in the variable random_number and we are using this variable as an output variable that can be referenced in other step as well.
pipeline:
  name: 
  identifier: project
  projectIdentifier: project
  orgIdentifier: default
  tags: {}
  stages:
    - stage:
        name: cs1
        identifier: cs1
        description: ""
        type: Custom
        spec:
          execution:
            steps:
              - step:
                  type: ShellScript
                  name: ShellScript_1
                  identifier: ShellScript_1
                  spec:
                    shell: Bash
                    executionTarget: {}
                    source:
                      type: Inline
                      spec:
                        script: random_number=$((1 + $RANDOM % 100))
                    environmentVariables: []
                    outputVariables:
                      - name: random_num
                        type: String
                        value: random_number
                  timeout: 10m
                  strategy:
                    matrix:
                      groups:
                        - name: firstGroup
                        - name: secondGroup
                        - name: thirdGroup
Second step
In step 2, we are using json.format and jq to extract those values and add them together.
              - step:
                  type: ShellScript
                  name: ShellScript_2
                  identifier: ShellScript_2
                  spec:
                    shell: Bash
                    executionTarget: {}
                    source:
                      type: Inline
                      spec:
                        script: |-
                          t='<+json.format(<+pipeline.stages.cs1.spec.execution.steps.ShellScript_1>)>'
                          echo $t | jq 'to_entries | map(select(.key | startswith("ShellScript_"))) | .[].value.outcome.output.outputVariables.random_num' | jq -s 'map(tonumber) | add'
                    environmentVariables: []
                    outputVariables: []
                  timeout: 10m
