使用 CloudFormation 时,AWS Lambda 函数缺少由 websocket 网关触发

AWS Lambda function is missing trigger by websocket gateway when using CloudFormation

我正在尝试 set-up 到 AWS 中 Lambda 函数的 websocket 网关。当我手动执行此操作时,我可以成功部署 websocket 并使用 wscat 进行试用。但是我想使用 CloudFormation 构建架构。

我的 CloudFormation yaml 文件的结构如下所示:

AWSTemplateFormatVersion: '2010-09-09'
Parameters:
  lambdaRole:
    Type: String
    Default: ...
  backendRole:
    Type: String
    Default: ...
  lambdaImage:
    Type: String
    Default: ...
Resources:
    MyLambdaFunction:
        Type: AWS::Lambda::Function
        Properties:
            Code:
                ImageUri: !Sub ${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/${backendRole}:${lambdaImage}
            Description: lambda connect function
            FunctionName: myLambdaFunction
            MemorySize: 128
            Role: !Sub arn:aws:iam::${AWS::AccountId}:role/${lambdaRole}
            Timeout: 3
            PackageType: Image
    MyWebSocket:
        Type: AWS::ApiGatewayV2::Api
        Properties:
            Name: MyWebSocket
            ProtocolType: WEBSOCKET
            RouteSelectionExpression: $request.body.action
    MyIntegration:
        Type: AWS::ApiGatewayV2::Integration
        Properties:
            ApiId: !Ref MyWebSocket
            Description: Lambda Integration
            IntegrationType: AWS_PROXY
            IntegrationUri: !Join
                - ''
                - - 'arn:'
                  - !Ref 'AWS::Partition'
                  - ':apigateway:'
                  - !Ref 'AWS::Region'
                  - ':lambda:path/2015-03-31/functions/'
                  - !GetAtt MyLambdaFunction.Arn
                  - /invocations
            IntegrationMethod: POST
    MyConnectRoute:
        Type: AWS::ApiGatewayV2::Route
        Properties:
            ApiId: !Ref MyWebSocket
            RouteKey: $connect
            Target: !Join
            - /
            - - integrations
              - !Ref MyIntegration
    MyDefaultRoute:
        Type: AWS::ApiGatewayV2::Route
        Properties:
            ApiId: !Ref MyWebSocket
            RouteKey: $default
            Target: !Join
            - /
            - - integrations
              - !Ref MyIntegration     
    MyResponseRoute:
        Type: AWS::ApiGatewayV2::Route
        Properties:
            ApiId: !Ref MyWebSocket
            RouteKey: add
            RouteResponseSelectionExpression: $default
            Target: !Join
            - /
            - - integrations
              - !Ref MyIntegration
    MyRouteResponse:
        Type: AWS::ApiGatewayV2::RouteResponse
        Properties:
            RouteId: !Ref MyResponseRoute
            ApiId: !Ref MyWebSocket
            RouteResponseKey: $default
    MyIntegrationResponse:
      Type: AWS::ApiGatewayV2::IntegrationResponse
      Properties:
        IntegrationId: !Ref MyIntegration
        IntegrationResponseKey: /201/
        ApiId: !Ref MyWebSocket
    testStage:
        Type: AWS::ApiGatewayV2::Stage
        DependsOn:
        - MyConnectRoute
        - MyDefaultRoute
        - MyResponseRoute
        Properties: 
            ApiId: !Ref MyWebSocket
            StageName: testStage
    MyDeployment:
        Type: AWS::ApiGatewayV2::Deployment
        Properties:
            ApiId: !Ref MyWebSocket
            Description: My Deployment
            StageName: !Ref testStage

堆栈的构建没有任何错误,并且(几乎)一切看起来都符合我的预期。然而,除了在手动构建版本中,将 Lambda 函数集成到 websocket 中似乎没有为 Lambda 函数添​​加所需的触发器。当我手动将 Lambda 函数添​​加到 API 网关时,这会自动添加触发器。

我需要在 CloudFormation Yaml 文件中更改什么才能将触发器添加到 Lambda 函数?

当 Lambda 函数手动添加到 API 网关时,触发器自动添加到 Lambda 函数:

使用 CloudFormation 将 Lambda 函数添​​加到 API 网关时未添加触发器:

我猜你需要 AWS::Lambda::Permission 资源,例如:

  LambdaPermission:
    Type: AWS::Lambda::Permission
    Properties:
      Action: lambda:invokeFunction
      FunctionName: !GetAtt MyLambdaFunction.Arn
      Principal: apigateway.amazonaws.com
      SourceArn:
        Fn::Join:
        - ''
        - - 'arn:aws:execute-api:'
          - Ref: AWS::Region
          - ":"
          - Ref: AWS::AccountId
          - ":"
          - Ref: MyWebSocket
          - "/*"

它可能需要一些调整 - 它适用于 AWS::ApiGateway::RestApi,不确定 AWS::ApiGatewayV2::Api 是否有区别。

看起来您可能只需要包含 api 网关的 Lambda 权限。

LambdaFunctionPermission:
  Type: "AWS::Lambda::Permission"
  Properties:
    Action: "lambda:InvokeFunction"
    Principal: apigateway.amazonaws.com
    FunctionName: !Ref MyLambdaFunction
  DependsOn: MyWebSocket

如果您在创建 Cloudformation 时遇到问题,那么创建一个用于日志记录的帐户配置并像这样链接网关资源的 dependsOn 顺序可能会有所帮助。

  ApiGwAccountConfig:
    Type: "AWS::ApiGateway::Account"
    Properties:
      CloudWatchRoleArn: !GetAtt "rRoleForCloudtrail.Arn"
  rRoleForCloudtrail:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
        - Effect: Allow
          Principal:
            Service:
            - apigateway.amazonaws.com
          Action:
          - sts:AssumeRole
      Policies:
        -
          PolicyName: "apigateway-cloudwatch"
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              -
                Effect: "Allow"
                Action: 
                  - logs:CreateLogGroup
                  - logs:CreateLogStream
                  - logs:DescribeLogGroups
                  - logs:DescribeLogStreams
                  - logs:PutLogEvents
                  - logs:GetLogEvents
                  - logs:FilterLogEvents
                Resource: "*"
  testStage:
    Type: AWS::ApiGatewayV2::Stage
    Properties: 
        ApiId: !Ref MyWebSocket
        StageName: testStage
    DefaultRouteSettings:
      DetailedMetricsEnabled: true
      LoggingLevel: INFO
      DataTraceEnabled: true
  DependsOn: ApiGwAccountConfig

  MyDeployment:
    Type: AWS::ApiGatewayV2::Deployment
    DependsOn:
    - MyConnectRoute
    - MyDefaultRoute
    - MyResponseRoute
    Properties:
        ApiId: !Ref MyWebSocket
        Description: My Deployment
        StageName: !Ref testStage