Search code examples
jsonamazon-web-servicesjsonpathstate-machineaws-step-functions

Reference value from state’s input, using JSONPath syntax in a SSMSendCommand API step through parameter which expects an array


I have on a AWS state machine the following step defined for api aws-sdk:ssm:sendCommand

{
  "Type": "Task",
  "Parameters": {
    "DocumentName.$": "$.result.DocumentName",
    "InstanceIds.$": "$..Dimensions[?(@.Name=~/.*InstanceId.*/)].Value",
    "MaxErrors": "0",
    "MaxConcurrency": "100%",
    "CloudWatchOutputConfig": {
      "CloudWatchLogGroupName": "diskspace-log",
      "CloudWatchOutputEnabled": true
    },
    "Parameters": {
      "workingDirectory": [
        ""
      ],
      "executionTimeout": [
        "3600"
      ],
      "commands": [
        "echo -------------------Mounting volume without signals $..Dimensions[?(@.Name=~/.*device.*/)].Value---------------------",
        "echo",
        "mount $..Dimensions[?(@.Name=~/.*device.*/)].Value"
      ]
    }
  }
}

The section: "commands": [] expects an array.

"commands" should accept input reference as any other parameter in the schema, so in theory will be posible to use json path parameters (Example: "size.$": "$.product.details.size") for referencing needed parameters from input.

https://docs.aws.amazon.com/step-functions/latest/dg/input-output-inputpath-params.html

This following example works without using input referencing :

"commands": [
"echo -------------------Mounting /dev/ebs---------------------",
"echo",
"mount /dev/ebs"
]

But I need to reference from input, hardcoded values won't work for me. I tried, but no working.

     "commands": [
        "echo -------------------Mounting volume without signals $..Dimensions[?(@.Name=~/.*device.*/)].Value---------------------",
        "echo",
        "mount $..Dimensions[?(@.Name=~/.*device.*/)].Value"
      ]

Also tried, not working also:

"commands.$": "States.Array(States.Format('echo -------------------Mounting volume without signals {} ---------------------', $..Dimensions[?(@.Name=~/.*device.*/)].Value),'echo',States.Format('mount {}', $..Dimensions[?(@.Name=~/.*device.*/)].Value))"

I believe some of the provided intrinsic functions will help on achieving the expected result but I'm lost on how to properly set up the syntax.

https://docs.aws.amazon.com/step-functions/latest/dg/amazon-states-language-intrinsic-functions.html#asl-intrsc-func-arrays

The step calls a RunShellScript type of documentCommand. And executes the commands provided on parameters in the step of the state machine.

I got on the output:

States.Format('echo -------------------Mounting volume without signals {} ---------------------', $..Dimensions[?(@.Name=~/.*device.*/)].Value)'

Its not detecting the input reference, I expect to output.

-------------------Mounting volume without signals /dev/ebs ---------------------

and in the background execute:

mount /dev/ebs

Solution

  • I was able to send the commands through a Pass State Flow, here is the definition:

    {
      "Type": "Pass",
      "Next": "SendCommand",
      "ResultPath": "$.ForArgs",
      "Parameters": {
        "Params": {
          "Args": [
            {
              "Arg1": "ec2-metadata -i"
            },
            {
              "Arg2": "echo"
            },
            {
              "Arg3.$": "States.Format('echo -------------------Mounting volume without signals {} ---------------------', States.ArrayGetItem($..Dimensions[?(@.Name=~/.*device.*/)].Value, 0))"
            },
            {
              "Arg4": "echo"
            },
            {
              "Arg5.$": "States.Format('mount {}', States.ArrayGetItem($..Dimensions[?(@.Name=~/.*device.*/)].Value, 0))"
            },
            {
              "Arg6.$": "States.Format('echo Checking if device {} is mounted', States.ArrayGetItem($..Dimensions[?(@.Name=~/.*device.*/)].Value, 0))"
            },
            {
              "Arg7.$": "States.Format('if findmnt --source \"{}\" >/dev/null', States.ArrayGetItem($..Dimensions[?(@.Name=~/.*device.*/)].Value, 0))"
            },
            {
              "Arg8": "\tthen echo device is mounted"
            },
            {
              "Arg9": "\telse echo device is not mounted"
            },
            {
              "Arg10": "fi"
            }
          ]
        }
      }
    }
    

    Next on the sendCommandApi:

      "commands.$": "$.ForArgs.Params.Args[*][*]"