serverless-step-functions icon indicating copy to clipboard operation
serverless-step-functions copied to clipboard

Support lambda-less definitions with ${} style resource references

Open justin-ellevation opened this issue 3 years ago • 0 comments

This is a Feature Proposal to support Lambda-less definitions with references to other resources (AWS Batch)

Description

If you create a serverless application that is comprised only of a stepFunctions block and a resources block (that defines some stuff in AWS Batch via a CF yml file), you are unable to refer to the CloudFormation Resources via ${Name} and have that result in the ARN of that resource. Much of this setup was inspired from this blog post and this code.

Here's a snipped of a stepFunction that should reproduce the issue:

resources:
    - ${file(batch.yml)}

stepFunctions:
  validate: true
  stateMachines:
    TestPipeline:
      name: ${self:service}-StepFunction-${opt:stage}
      useExactVersion: true
      definition:
        StartAt: PipelineStep
        States:
          PipelineStep:
            Type: Task
            Resource: arn:aws:states:::batch:submitJob.sync
            Parameters:
              JobName: MyJob-${opt:stage}
              JobDefinition: "${SlsJobDefinition}"
              JobQueue: "${SlsJobQueue}"

where batch.yml contains CloudFormation to setup an AWS Batch environment.

This generates a serverless state file that has a DefinitionString for this step function that is a string of the serialized JSON. This means that CloudFormation will not do any replacement of the ${SlsJobDefinition} and ${SlsJobQueue} parameters with their respective ARNs. Instead the literal values as they appear here are used in the Step Function definition.

You can trigger this behavior if you also have a state that references a lambda functions. The code here shows more, but basically the raw json string gets wrapped in a Fn::Sub call in the generated CF template, but only when there are references to lambdas or there are pseudo parameters.

I did try to convert the ${} references to #{} references which did trigger a new path, but unfortunately failed because of this block of code. Basically if you've specified useExactVersion, and we're wrapping the json in that call, the assumption is that the Fn::Sub key contains an object you can reference [1] on, but if you go down this branch, and have no lambda references and have some pseudo parameters, the value of the Fn::Sub key is a string (the json string)...and so you get an error like this:

  TypeError: Cannot assign to read only property '1' of string '{
      ...more json here
  '

We were able to work around this fully by changing our definition to the following:

stepFunctions:
  validate: true
  stateMachines:
    TestPipeline:
      name: ${self:service}-StepFunction-${opt:stage}
      #useExactVersion: true
      definition:
        StartAt: PipelineStep
        States:
          PipelineStep:
            Type: Task
            Resource: arn:aws:states:::batch:submitJob.sync
            Parameters:
              JobName: MyJob-${opt:stage}
              JobDefinition: "#{SlsJobDefinition}"
              JobQueue: "#{SlsJobQueue}"

Basically switch to using pseudo-parameter style references (no long required in our version of sls) and the disabling of exact versioning (which for us, isn't a deal breaker).

Additional Data

Here's some output from a failure run:

Operating System:          linux
Node Version:              17.3.0
Framework Version:         2.65.0
Plugin Version:            5.5.1
SDK Version:               4.3.0
Components Version:        3.18.1

justin-ellevation avatar Dec 22 '21 00:12 justin-ellevation