History

AWS Step Functions are cool. Express Step Functions have always felt like they had the potential to be cool, but were missing some key features when they launched. I think they’re worth revisiting in light of recent releases. A timeline:

These are the key launches that make it possible to have an express workflow invoke another express workflow.

What about the existing feature?

In May 2020, SFN released arn:aws:states:::states:startExecution.sync:2. This was a welcome improvement over the original (launched in August 2019) as the JSON output is parsed. But it only works when invoked by a standard workflow, because .sync isn’t support for express workflows. And even from a standard workflow, it can’t synchronously invoke an express step function because that is a different API: StartSyncExecution vs StartExecution. EDIT: See addendum.

Just show me

Resources:
  Parent:
    Type: AWS::StepFunctions::StateMachine
    Properties:
      StateMachineType: EXPRESS
      RoleArn: !GetAtt ParentRole.Arn
      Definition:
        StartAt: Example sync step
        States:
          Example sync step:
            Type: Task
            End: true
            Parameters:
              StateMachineArn: !Ref Child
              Input.$: $
            Resource: arn:aws:states:::aws-sdk:sfn:startSyncExecution
            ResultPath: $.ChildOutput
            OutputPath: $.ChildOutput
            ResultSelector:
              Output.$: States.StringToJson($.Output)

  Child:
    Type: AWS::StepFunctions::StateMachine
    Properties:
      StateMachineType: EXPRESS
      RoleArn: !GetAtt ChildRole.Arn
      Definition:
        StartAt: Hello
        States:
          Hello:
            Type: Pass
            End: true
            Result:
              Hello: World

  ParentRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Action: sts:AssumeRole
            Principal:
              Service: states.amazonaws.com
      Policies:
        - PolicyName: AllowStartChild
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: Allow
                Action: states:StartSyncExecution
                Resource: !Ref Child

  ChildRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Action: sts:AssumeRole
            Principal:
              Service: states.amazonaws.com
      Policies: []

It’s not a very useful couple of step functions, but it gets the point across. The parent state machine invokes the child state machine and then is able to process the child’s output as normal.

Addendum

I made a mistake in the first version of this blog post. Calling an express workflow synchronously from a standard workflow actually works just fine. In fact, there’s even a sample of this in the AWS docs and they call it “selective checkpointing”. But if you use the ...:sync:2 approach from a standard workflow, the parent IAM role instead needs to be:

  ParentRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Action: sts:AssumeRole
            Principal:
              Service: states.amazonaws.com
      Policies:
        - PolicyName: AllowStartChild
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: Allow
                Action: states:StartExecution
                Resource: !Ref Child
              - Effect: Allow
                Action:
                  - states:DescribeExecution
                  - states:StopExecution
                Resource: !Ref Child
              - Effect: Allow
                Action:
                  - events:PutTargets
                  - events:PutRule
                  - events:DescribeRule
                Resource: !Sub arn:aws:events:${AWS::Region}:${AWS::AccountId}:rule/StepFunctionsGetEventsForStepFunctionsExecutionRule