aws / serverless-application-model

The AWS Serverless Application Model (AWS SAM) transform is a AWS CloudFormation macro that transforms SAM templates into CloudFormation templates.
https://aws.amazon.com/serverless/sam
Apache License 2.0
9.33k stars 2.38k forks source link

Cannot set HttpApi Event Type on AWS::Serverless::StateMachine #1851

Open davingreen opened 3 years ago

davingreen commented 3 years ago

I'm attempting to configure step functions to be triggered from an HttpApi (rather than a REST api). Here is my template:

  APIGatewayDeployment:
    Type: AWS::Serverless::HttpApi
    Properties:
      StageName: !Ref pStage
    DefinitionBody:
        info:
          title: !Ref AWS::StackName
          version: "1.0"
        openapi: 3.0.1

  MyStateMachine:
    Type: AWS::Serverless::StateMachine
    Properties:
      DefinitionUri: statemachines/MyStateMachine.asl.json
      DefinitionSubstitutions:
        MyFunction: !GetAtt MyFunction.Arn
      Events:
        HttpAPIGateway:
          Type: HttpApi
          Properties:
            Path: /inbound
            Method: post
            ApiId: !Ref APIGatewayDeployment
      Policies: 
        - LambdaInvokePolicy:
            FunctionName: !Ref MyFunction

This builds, but fails validation or deployment with the following: Error: Failed to create changeset for the stack: , ex: Waiter ChangeSetCreateComplete failed: Waiter encountered a terminal failure state Status: FAILED. Reason: Transform AWS::Serverless-2016-10-31 failed with: Invalid Serverless Application Specification document. Number of errors found: 1. Resource with id [MyStateMachine] is invalid. Event with id [HttpAPIGateway] is invalid. Resource dict has missing or invalid value for key Type. Event Type is: HttpApi.

If I've done something wrong, let me know! I've been unable to find helpful documentation about using HTTP rather than REST with step functions.

peterwmwong commented 3 years ago

This would help our team aswell if HttpApi was a supported event source. We tried to work around this by defining the necessary CloudFormation resources manually (extending the example above)...

MyStateMachine:
  Type: AWS::Serverless::StateMachine
  # ... The State Machine configuration

MyStateMachineIntegrationRole:
  Type: AWS::IAM::Role
  Properties:
    AssumeRolePolicyDocument:
      Version: 2012-10-17
      Statement:
        - Sid: MyStateMachineIntegrationStartExecuteStateMachines
          Effect: Allow
          Principal:
            Service:
              - apigateway.amazonaws.com
          Action:
            - states:StartExecution

MyStateMachineIntegration:
  Type: AWS::ApiGatewayV2::Integration
  Properties:
    ApiId: !Ref HttpApiGateway
    IntegrationType: AWS_PROXY
    IntegrationSubtype: StepFunctions-StartExecution
    PayloadFormatVersion: 1.0
    CredentialsArn: !GetAtt MyStateMachineIntegrationRole.Arn
    RequestParameters:
      Input: "$request.body"
      StateMachineArn: !Ref MyStateMachine

MyStateMachineRoute:
  Type: AWS::ApiGatewayV2::Route
  DependsOn:
    - MyStateMachineIntegration
  Properties:
    ApiId: !Ref HttpApiGateway
    RouteKey: "POST /startMyStateMachine"
    AuthorizationType: NONE
    Target: !Join
      - /
      - - integrations
        - !Ref MyStateMachineIntegration

... Which worked for me, BUT is not secure as there doesn't seem to be a way to link the Authorizer created by SAM...

HttpApiGateway:
  Type: AWS::Serverless::HttpApi
  Properties:
    # ... more configuration
    Auth:
      Authorizers:
        OAuth2Authorizer:  # <<<-- How do I reference the Authorizer ID created here in my `AWS::ApiGatewayV2::Route`?
          # ...more configuration

Related: #1050

In the end, I've had to fall back to create a separate lambda (connected to the Http Api Gateway) that starts the state machine. Alternatively, maybe I could have defined another Authorizer with the same exact configuration as the one defined by SAM and reference that. In either case, not ideal, and would love for this to just work™.

svenemtell commented 3 years ago

Any news on this? It would be really handy to create an HttpApi <-> StateMachine integration the way @davingreen suggested!

Jacco commented 3 years ago

Am busy implementing this. In the console of the HttpApi I noticed that there are actually 3 integrations with StepFunctions: start (default), stop and startSync. I thought to add an Action property to the event source. See example below.

APIGatewayDeployment:
    Type: AWS::Serverless::HttpApi
    Properties:
      StageName: !Ref pStage
    DefinitionBody:
        info:
          title: !Ref AWS::StackName
          version: "1.0"
        openapi: 3.0.1

  MyStateMachine:
    Type: AWS::Serverless::StateMachine
    Properties:
      DefinitionUri: statemachines/MyStateMachine.asl.json
      DefinitionSubstitutions:
        MyFunction: !GetAtt MyFunction.Arn
      Events:
        HttpAPIGateway:
          Type: HttpApi
          Properties:
            Path: /inbound
            Method: post
            Action: startSync
            ApiId: !Ref APIGatewayDeployment
      Policies: 
        - LambdaInvokePolicy:
            FunctionName: !Ref MyFunction

Also for stop you need to pass the execution arn in the request to the API. This can be different depending on the method. Should it be cusomizable?

Also during testing I saw that if the same step function is targetted multiple times for same api a role will be created for each event. Should this be optimized?

Also should we have different roles for the three actions or one generic role that allows all three for the state machine.

Jacco commented 3 years ago

I have added some discussion in the conversation of the PR. Will be happy to work on improvements.

NickLiffen commented 3 years ago

@Jacco any news on this, something like the above would be incredibly useful 💯

brysontyrrell commented 2 years ago

Went to open an issue asking for support for HttpApi event source for Step Functions and found this issue. Shared on Twitter. Hoping to get some movement. There are patterns on Serverlessland.com that show how to long-hand it, but I would like to have it work with HTTP APIs just like my Lambda Functions do in SAM.