Search code examples
amazon-web-servicesaws-cloudformationaws-api-gatewayaws-step-functions

AWS CloudFormation ApiGatewayV2 Target property required


I'm attempting to build a CF stack that essentially does what an AWS guide can do through the console: https://docs.aws.amazon.com/step-functions/latest/dg/tutorial-api-gateway.html

I'm able to create it manually through the console, and it works. Now I'd like to use ApiGatewayV2, because it hs the AutoDeploy feature, whereas the RestApi does not. My template.yml is (only the relevant bits):

  GatewayV2:
    Type: AWS::ApiGatewayV2::Api
    Properties:
      Name: !Sub ${AWS::StackName}-GatewayV2
      Description: API Gateway for integrations
      ProtocolType: HTTP
      CredentialsArn: !GetAtt SubmitOrderResourceRole.Arn
      RouteKey: $default
  StageV2:
    Type: AWS::ApiGatewayV2::Stage
    Properties:
      ApiId: !Ref GatewayV2
      DeploymentId: !Ref DeploymentV2
      StageName: $default
      AutoDeploy: true
  DeploymentV2:
    Type: AWS::ApiGatewayV2::Deployment
    DependsOn:
      - RouteV2
    Properties:
      ApiId: !Ref GatewayV2
      Description: Deployment for the API Gateway
  RouteV2:
    Type: AWS::ApiGatewayV2::Route
    Properties:
      ApiId: !Ref GatewayV2
      RouteKey: POST /orders
      Target: !Join
        - /
        - - integrations
          - !Ref IntegrationV2
  IntegrationV2:
    Type: AWS::ApiGatewayV2::Integration
    Properties:
      ApiId: !Ref GatewayV2
      IntegrationType: AWS_PROXY
      CredentialsArn: !GetAtt SubmitOrderResourceRole.Arn
      IntegrationMethod: POST
      IntegrationUri: !Sub arn:aws:apigateway:${AWS::Region}:states:action/StartSyncExecution
      PassthroughBehavior: NEVER
      RequestTemplates:
        application/json: !Sub |
          {
            "input": "$util.escapeJavaScript($input.json('$'))",
            "stateMachineArn": "${SubmitOrderStateMachine}"
          }
      ResponseParameters:
        - StatusCode: 200
          ResponseTemplates:
            application/json: |
              if(!$input.json('$.status').toString().equals('"SUCCEEDED"'))
              set($context.responseOverride.status = 500)
              end

However, when I attempt to deploy this, I get an error back:

Resource handler returned message: "Target is a required property in this context

If I try to use the Target property of the ApiGatewayV2 and set it to the ARN of my step function:

  GatewayV2:
    Type: AWS::ApiGatewayV2::Api
    Properties:
      Name: !Sub ${AWS::StackName}-GatewayV2
      Description: API Gateway for integrations
      ProtocolType: HTTP
      CredentialsArn: !GetAtt SubmitOrderResourceRole.Arn
      RouteKey: $default
      Target: !Sub integrations/${SubmitOrderStateMachine}

I instead get this error:

Resource handler returned message: "Target only supports HTTP Proxy or Lambda Proxy

Does anyone know why the Target property is required "in this context", and how I can bypass it? Its docs say it's only for the quick-create features, which I don't need to do here. I'm happy to define the specific deployments, stages, routes, and integrations to make it work.


Solution

  • AWS::ApiGatewayV2::Api only supports two kinds of APIs: WebSockets and HTTPS via quick create.

    The older V1 API Gateway allows much more control over the process, and does not use quick create. You can use a Method rather than an Integration to link it to the state machine; it's complicated but possible.

    I suggest creating the Gateway using the V1 definition instead (along with any deployments/stages/routes):

    Gateway:
      Type: AWS::ApiGateway::RestApi
      Properties:
          Name:  !Sub ${AWS::StackName}-Gateway
          EndpointConfiguration:
              Types:
                - REGIONAL
    

    With the gateway created, you can now add a Method to link the Gateway to the Step Function:

    GatewayMethod:
      Type: AWS::ApiGateway::Method
      Properties:
        ResourceId:
          Ref: GatewayResource
        RestApiId:
          Ref: Gateway
        ApiKeyRequired: true
        AuthorizationType: NONE
        HttpMethod: POST
        Integration:
          Type: AWS
          IntegrationHttpMethod: POST
          Uri: arn:aws:apigateway:us-east-1:states:action/StartSyncExecution
          Credentials:
           ...
          RequestTemplates:
           ...
          IntegrationResponses:
         ...
        MethodResponses:
        - StatusCode: 200
    

    The above was taken from https://github.com/antstackio/api-gateway-to-synchronous-step-function which is a more in-depth explanation. The article explains more but this should be enough to get you started.

    This approach should allow you to create a gateway without using quick create. Note there's also a difference between V1 HTTPS and REST APIs, but both should work with a step function.