Как можно вернуть двоичный контент через AWS Lambda через API Gateway и CloudFront в режиме AWS_PROXY? - PullRequest
4 голосов
/ 06 января 2020

Как я могу подготовить AWS API Gateway, предоставляемый CloudFront (чтобы я мог перенаправлять HTTP на HTTPS) и поддержанный AWS лямбда-функцией, используя тип интеграции AWS_PROXY используя CloudFormation?

Ниже приведен шаблон CloudFormation, показывающий, что я пробовал. Он включает в себя

  • Простую лямбда-функцию, которая возвращает ожидаемый формат вывода для лямбды в режиме AWS_PROXY.
    • Значение isBase64Encoded установлено в значение True.
  • A AWS::ApiGateway::RestApi CloudFormation ресурс, который включает свойство BinaryMediaTypes, содержащее значение *~1*.

Я прочитал это AWS сообщение на форуме, AWS_PROXY и бинарные ответы , но не понял, что я делаю неправильно. Сообщение на форуме включает людей, публикующих информацию как о режиме AWS_PROXY, так и о других режимах, так что это немного сбивает с толку.

Эта страница AWS do c, Support Binary Payloads in API Gateway, Я полагаю, что речь идет о режимах, отличных от AWS_PROXY, поскольку речь идет об установке свойства IntegrationResponses , которое требует использования StatusCode , который соответствует MethodResponse StatusCode.

Вот шаблон CloudFormation, в котором обнаружена проблема. Вы можете воспроизвести его с помощью этих шагов

  1. Предоставить сертификат ACM для доменного имени DNS в существующей зоне Route53 в вашей учетной записи
  2. Укажите имя домена, имя зоны (оканчивающееся на символ ".") и ACN ARN в качестве параметров стека CloudFormation
  3. Раскрутите стек CloudFormation с помощью приведенного ниже шаблона (поскольку он использует CloudFront, это может занять 30 минут)
  4. curl URL-адрес шлюза API

Если это работает правильно, вы получите ответ HTTP в виде двоичного png, вместо этого вы получите ответ base64.

AWSTemplateFormatVersion: 2010-09-09
Description: Test binary responses with AWS_PROXY mode
Parameters:
  CustomDomainName:
    Type: String
    Description: The custom domain name to use for the API
    Default: ''
    # AWS::ApiGateway::DomainName can not contain any uppercase characters
    AllowedPattern: '^[^A-Z]*$'
    ConstraintDescription: must not contain any uppercase characters
  DomainNameZone:
    Type: String
    Description: The Route53 DNS zone containing the custom domain name
    Default: ''
  CertificateArn:
    Type: String
    Description: The ARN of the AWS ACM Certificate for your custom domain name
    Default: ''
Resources:
  TestFunctionRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - lambda.amazonaws.com
            Action:
              - sts:AssumeRole
      Policies:
        - PolicyName: AllowLambdaLogging
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                  - logs:CreateLogGroup
                  - logs:CreateLogStream
                  - logs:PutLogEvents
                Resource: '*'
  TestFunction:
    Type: AWS::Lambda::Function
    Properties:
      Description: Test Function
      Code:
        ZipFile: |
          def lambda_handler(event, context):
            body = 'iVBORw0KGgoAAAANSUhEUgAAABEAAAAKCAYAAABSfLWiAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAA0gAAANIBBp0MHQAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAHgSURBVCiRlY47aFNxGEfP97/p05SWYhXfEHMjNZobuChYk1iwUCKKiqSjj0XpIM46uDgUQdxqk0lUHJwsiEPtoEmtgxhMIx2StFJBhA4tOCTVPO7n0C5uesbDj8NPAEJO4oXCLqDHU3PbktYJhM/lwty07SRmEHlQKWRn7Uh8VlRvqDFpoEdgo7yQO+0DqP80V1ZW3v0KOcMxI95dMFOqnD8YGfoAckCUZMCNlWhKvxoGxaNWLuZGAQUQwNhOfEJFjhqPugo7u7RzZEN+50HvgO4R5KKKPkVlb9VXfbit5X+Cp2FBn5WLc/dNyBkeAkksFXJnWurdA6xi8U0VqIBc89R6q0hVPLmgtF7+yOdrlUI2ZdXb4hhzKRQ95frENL6qZ+2zo/FHqHQAA6RSlpZWp0WYWC5mF4NO4j3C1aWF+UXbiZ0VZKxFo4pitTcbywAE3JHeQDRhAxIOh9vZxITDw34A13Xbdrtu95Yn4Mb2HzoSjwSDyQ4A0SlOyjjz/Af6mE7q3AQGgW4D1DTDc01zWTP0/lPlG02ULxgmUfoEQCfx4+MWMI5SQvi0NVpDWcejC6EfsBGOA4cR0vh4RZNz8tfNzVgSYRTlGLADGADWge/AR4QZ+ngtY9Q1w3aus/YHPCW0c1bW92YAAAAASUVORK5CYII='
            return {
              'headers': {'Content-Type': 'image/png'},
              'statusCode': 200,
              'isBase64Encoded': True,
              'body': body}
      Handler: index.lambda_handler
      Runtime: python3.7
      Role: !GetAtt TestFunctionRole.Arn
      Timeout: 900
  TestFunctionLogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      # Let's hope that the Lambda function doesn't execute before this LogGroup
      # resource is created, creating the LogGroup with no expiration and
      # preventing this resource from creating
      LogGroupName: !Join [ '/', ['/aws/lambda', !Ref 'TestFunction' ] ]
      RetentionInDays: 1
  TestRoute53RecordSet:
    Type: AWS::Route53::RecordSet
    Properties:
      AliasTarget:
        DNSName: !GetAtt TestCloudFrontDistribution.DomainName
        HostedZoneId: Z2FDTNDATAQYW2  # https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-route53-aliastarget-1.html
      Comment: Bind the custom domain name to the Test CloudFront fronted API Gateway
      HostedZoneName: !Ref DomainNameZone
      Name: !Ref CustomDomainName
      Type: A
  TestApi:
    Type: AWS::ApiGateway::RestApi
    Properties:
      Name: Test
      BinaryMediaTypes:
        - '*~1*'
      Description: Test API
      FailOnWarnings: true
      EndpointConfiguration:
        Types:
          - REGIONAL
  TestApiGatewayDomainName:
    # The ApiGateway requires a custom domain name, despite sitting behind
    # CloudFront. This is because we want to pass all ( * ) HTTP headers
    # through CloudFront and onto API Gateway. If we didn't set a custom domain
    # name on the API Gateway, the "Host" header passed through from CloudFront
    # to API Gateway would be for the custom domain, but API Gateway, which uses
    # SNI, wouldn't know which TLS certificate to use in the handshake because
    # API Gateway would have no record of that Host header. This would result in
    # API Gateway being unable to setup a TLS connection with the inbound
    # CloudFront connection attempt, API Gateway writing no logs about this
    # fact, and CloudFront returning to the user an error of
    # {"message":"Forbidden"}
    # If we weren't passing the "Host" header from CloudFront to API Gateway
    # this resource wouldn't be needed
    Type: AWS::ApiGateway::DomainName
    Properties:
      # Uppercase letters are not supported in DomainName
      DomainName: !Ref CustomDomainName
      EndpointConfiguration:
        Types:
          - REGIONAL
      RegionalCertificateArn: !Ref CertificateArn
      SecurityPolicy: TLS_1_2
  TestBasePathMapping:
    Type: AWS::ApiGateway::BasePathMapping
    Properties:
      # BasePath:   # Not specifying this so that we have no base path
      DomainName: !Ref TestApiGatewayDomainName
      RestApiId: !Ref TestApi
      Stage: !Ref TestApiStage
  TestLambdaPermission:
    Type: AWS::Lambda::Permission
    Properties:
      Action: lambda:invokeFunction
      FunctionName: !GetAtt TestFunction.Arn
      Principal: apigateway.amazonaws.com
      SourceArn: !Join [ '', [ 'arn:aws:execute-api:', !Ref 'AWS::Region', ':', !Ref 'AWS::AccountId', ':', !Ref 'TestApi', '/*/*' ] ]
  TestApiStage:
    Type: AWS::ApiGateway::Stage
    Properties:
      DeploymentId: !Ref TestApiDeployment
      MethodSettings:
        - DataTraceEnabled: true
          HttpMethod: '*'
          ResourcePath: /*
      RestApiId: !Ref TestApi
  TestApiDeployment:
    Type: AWS::ApiGateway::Deployment
    DependsOn:
      - TestRequest
    Properties:
      RestApiId: !Ref TestApi
      StageName: DummyStage
      # Deployment with an Empty Embedded Stage
      # The following instructional text is no longer present in the AWS
      # documentation for AWS::ApiGateway::Deployment StageName and it's not
      # clear if it still applies.
      #
      # "Note This property is required by API Gateway. We recommend that you
      # specify a name using any value (see Examples) and that you don’t use
      # this stage. We recommend not using this stage because it is tied to
      # this deployment, which means you can’t delete one without deleting the
      # other. For example, if you delete this deployment, API Gateway also
      # deletes this stage, which you might want to keep. Instead, use the
      # AWS::ApiGateway::Stage resource to create and associate a stage with
      # this deployment."
  TestResource:
    Type: AWS::ApiGateway::Resource
    Properties:
      RestApiId: !Ref TestApi
      ParentId: !GetAtt TestApi.RootResourceId
      PathPart: '{proxy+}'
  TestRequest:
    DependsOn: TestLambdaPermission
    Type: AWS::ApiGateway::Method
    Properties:
      AuthorizationType: NONE
      HttpMethod: GET
      Integration:
        Type: AWS_PROXY
        # IntegrationHttpMethod is POST regardless of the HttpMethod for this resource
        # https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html#set-up-lambda-proxy-integration-using-cli
        # "For Lambda integrations, you must use the HTTP method of POST for the
        # integration request, according to the specification of the Lambda service
        # action for function invocations."
        IntegrationHttpMethod: POST
        Uri: !Join [ '', [ 'arn:aws:apigateway:', !Ref 'AWS::Region', ':lambda:path/2015-03-31/functions/', !GetAtt 'TestFunction.Arn', '/invocations' ] ]
      ResourceId: !Ref TestResource
      RestApiId: !Ref TestApi
  TestPOSTRequest:
    DependsOn: TestLambdaPermission
    Type: AWS::ApiGateway::Method
    Properties:
      AuthorizationType: NONE
      HttpMethod: POST
      Integration:
        Type: AWS_PROXY
        # https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html#set-up-lambda-proxy-integration-using-cli
        # "For Lambda integrations, you must use the HTTP method of POST for the
        # integration request, according to the specification of the Lambda service
        # action for function invocations."
        IntegrationHttpMethod: POST
        Uri: !Join [ '', [ 'arn:aws:apigateway:', !Ref 'AWS::Region', ':lambda:path/2015-03-31/functions/', !GetAtt 'TestFunction.Arn', '/invocations' ] ]
      ResourceId: !Ref TestResource
      RestApiId: !Ref TestApi
  TestRootRequest:
    # This resource is necessary to get API Gateway to respond to requests for the '/' path
    # Without it API Gateway will respond to requests for '/' with the error
    # {"message":"Missing Authentication Token"}
    # https://stackoverflow.com/q/46578615/168874
    # https://stackoverflow.com/q/52909329/168874
    DependsOn: TestLambdaPermission
    Type: AWS::ApiGateway::Method
    Properties:
      AuthorizationType: NONE
      HttpMethod: GET
      Integration:
        Type: AWS_PROXY
        # IntegrationHttpMethod is POST regardless of the HttpMethod for this resource
        # https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html#set-up-lambda-proxy-integration-using-cli
        # "For Lambda integrations, you must use the HTTP method of POST for the
        # integration request, according to the specification of the Lambda service
        # action for function invocations."
        IntegrationHttpMethod: POST
        Uri: !Join [ '', [ 'arn:aws:apigateway:', !Ref 'AWS::Region', ':lambda:path/2015-03-31/functions/', !GetAtt 'TestFunction.Arn', '/invocations' ] ]
      # ResourceId must use the RootResourceId attribute of the AWS::ApiGateway::RestApi
      # https://stackoverflow.com/a/56121914/168874
      ResourceId: !GetAtt TestApi.RootResourceId
      RestApiId: !Ref TestApi
  TestCloudFrontDistribution:
    Type: AWS::CloudFront::Distribution
    Properties:
      DistributionConfig:
        Comment: !Join [ ':', [!Ref 'AWS::StackName', 'Test']]
        DefaultCacheBehavior:
          AllowedMethods:
            - GET
            - HEAD
            - POST
            - DELETE
            - OPTIONS
            - PUT
            - PATCH
          Compress: true
          DefaultTTL: 0
          MinTTL: 0
          MaxTTL: 0
          ForwardedValues:
            Cookies:
              Forward: all
            QueryString: true
            Headers:
              - '*'
          TargetOriginId: TestCloudFrontOriginId
          ViewerProtocolPolicy: redirect-to-https
        # https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloudfront-distribution-distributionconfig.html#cfn-cloudfront-distribution-distributionconfig-defaultrootobject
        DefaultRootObject: ''  # "If you don't want to specify a default root object when you create a distribution, include an empty DefaultRootObject element."
        Enabled: true
        Aliases:
          - !Ref CustomDomainName
        HttpVersion: http2
        IPV6Enabled: true
        #Logging:
        #  Logging
        Origins:
          - CustomOriginConfig:
              OriginProtocolPolicy: https-only
              OriginSSLProtocols:
                - TLSv1.2
            DomainName: !GetAtt TestApiGatewayDomainName.RegionalDomainName
            Id: TestCloudFrontOriginId
            # OriginPath: !Join [ '', [ '/', !Ref 'TestApiStage' ] ]
        PriceClass: PriceClass_100  # US, Canada, Europe, Israel
        ViewerCertificate:
          AcmCertificateArn: !Ref CertificateArn
          MinimumProtocolVersion: TLSv1.2_2018
          SslSupportMethod: sni-only

1 Ответ

0 голосов
/ 01 апреля 2020

Я связался с AWS Службой поддержки, и после того, как многие взад и вперед обнаружили, что проблема в документации AWS.

Страница документации для AWS::ApiGateway::RestApi Тип ресурса CloudFormation неверно говорится

Косые черты должны быть экранированы с ~ 1. Например, image / png будет изображением ~ 1png в списке BinaryMediaTypes.

Оказывается, это не так, и значение, которое вы должны указать в поле BinaryMediaTypes, равно */*, а не *~1*. Это заставляет поле выглядеть следующим образом

Resources:
  TestApi:
    Type: AWS::ApiGateway::RestApi
    Properties:
      Name: Test
      BinaryMediaTypes:
        - '*/*'

Таким образом, с этим изменением шаблона в вопросе полученный стек правильно обслуживает двоичные ресурсы.

Я подтвердил, что это AWS do c page, Support Binary Payloads in API Gateway, действительно говорит о режимах, отличных от AWS_PROXY, и не относится к моему вопросу.

Шаблон с исправлением, которое я ' Это подтвержденные работы

AWSTemplateFormatVersion: 2010-09-09
Description: Test binary responses with AWS_PROXY mode
Parameters:
  CustomDomainName:
    Type: String
    Description: The custom domain name to use for the API
    Default: ''
    # AWS::ApiGateway::DomainName can not contain any uppercase characters
    AllowedPattern: '^[^A-Z]*$'
    ConstraintDescription: must not contain any uppercase characters
  DomainNameZone:
    Type: String
    Description: The Route53 DNS zone containing the custom domain name
    Default: ''
  CertificateArn:
    Type: String
    Description: The ARN of the AWS ACM Certificate for your custom domain name
    Default: ''
Resources:
  TestFunctionRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - lambda.amazonaws.com
            Action:
              - sts:AssumeRole
      Policies:
        - PolicyName: AllowLambdaLogging
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                  - logs:CreateLogGroup
                  - logs:CreateLogStream
                  - logs:PutLogEvents
                Resource: '*'
  TestFunction:
    Type: AWS::Lambda::Function
    Properties:
      Description: Test Function
      Code:
        ZipFile: |
          def lambda_handler(event, context):
            body = 'iVBORw0KGgoAAAANSUhEUgAAABEAAAAKCAYAAABSfLWiAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAA0gAAANIBBp0MHQAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAHgSURBVCiRlY47aFNxGEfP97/p05SWYhXfEHMjNZobuChYk1iwUCKKiqSjj0XpIM46uDgUQdxqk0lUHJwsiEPtoEmtgxhMIx2StFJBhA4tOCTVPO7n0C5uesbDj8NPAEJO4oXCLqDHU3PbktYJhM/lwty07SRmEHlQKWRn7Uh8VlRvqDFpoEdgo7yQO+0DqP80V1ZW3v0KOcMxI95dMFOqnD8YGfoAckCUZMCNlWhKvxoGxaNWLuZGAQUQwNhOfEJFjhqPugo7u7RzZEN+50HvgO4R5KKKPkVlb9VXfbit5X+Cp2FBn5WLc/dNyBkeAkksFXJnWurdA6xi8U0VqIBc89R6q0hVPLmgtF7+yOdrlUI2ZdXb4hhzKRQ95frENL6qZ+2zo/FHqHQAA6RSlpZWp0WYWC5mF4NO4j3C1aWF+UXbiZ0VZKxFo4pitTcbywAE3JHeQDRhAxIOh9vZxITDw34A13Xbdrtu95Yn4Mb2HzoSjwSDyQ4A0SlOyjjz/Af6mE7q3AQGgW4D1DTDc01zWTP0/lPlG02ULxgmUfoEQCfx4+MWMI5SQvi0NVpDWcejC6EfsBGOA4cR0vh4RZNz8tfNzVgSYRTlGLADGADWge/AR4QZ+ngtY9Q1w3aus/YHPCW0c1bW92YAAAAASUVORK5CYII='
            return {
              'headers': {'Content-Type': 'image/png'},
              'statusCode': 200,
              'isBase64Encoded': True,
              'body': body}
      Handler: index.lambda_handler
      Runtime: python3.7
      Role: !GetAtt TestFunctionRole.Arn
      Timeout: 900
  TestFunctionLogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      # Let's hope that the Lambda function doesn't execute before this LogGroup
      # resource is created, creating the LogGroup with no expiration and
      # preventing this resource from creating
      LogGroupName: !Join [ '/', ['/aws/lambda', !Ref 'TestFunction' ] ]
      RetentionInDays: 1
  TestRoute53RecordSet:
    Type: AWS::Route53::RecordSet
    Properties:
      AliasTarget:
        DNSName: !GetAtt TestCloudFrontDistribution.DomainName
        HostedZoneId: Z2FDTNDATAQYW2  # https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-route53-aliastarget-1.html
      Comment: Bind the custom domain name to the Test CloudFront fronted API Gateway
      HostedZoneName: !Ref DomainNameZone
      Name: !Ref CustomDomainName
      Type: A
  TestApi:
    Type: AWS::ApiGateway::RestApi
    Properties:
      Name: Test
      BinaryMediaTypes:
        - '*/*'
      Description: Test API
      FailOnWarnings: true
      EndpointConfiguration:
        Types:
          - REGIONAL
  TestApiGatewayDomainName:
    # The ApiGateway requires a custom domain name, despite sitting behind
    # CloudFront. This is because we want to pass all ( * ) HTTP headers
    # through CloudFront and onto API Gateway. If we didn't set a custom domain
    # name on the API Gateway, the "Host" header passed through from CloudFront
    # to API Gateway would be for the custom domain, but API Gateway, which uses
    # SNI, wouldn't know which TLS certificate to use in the handshake because
    # API Gateway would have no record of that Host header. This would result in
    # API Gateway being unable to setup a TLS connection with the inbound
    # CloudFront connection attempt, API Gateway writing no logs about this
    # fact, and CloudFront returning to the user an error of
    # {"message":"Forbidden"}
    # If we weren't passing the "Host" header from CloudFront to API Gateway
    # this resource wouldn't be needed
    Type: AWS::ApiGateway::DomainName
    Properties:
      # Uppercase letters are not supported in DomainName
      DomainName: !Ref CustomDomainName
      EndpointConfiguration:
        Types:
          - REGIONAL
      RegionalCertificateArn: !Ref CertificateArn
      SecurityPolicy: TLS_1_2
  TestBasePathMapping:
    Type: AWS::ApiGateway::BasePathMapping
    Properties:
      # BasePath:   # Not specifying this so that we have no base path
      DomainName: !Ref TestApiGatewayDomainName
      RestApiId: !Ref TestApi
      Stage: !Ref TestApiStage
  TestLambdaPermission:
    Type: AWS::Lambda::Permission
    Properties:
      Action: lambda:invokeFunction
      FunctionName: !GetAtt TestFunction.Arn
      Principal: apigateway.amazonaws.com
      SourceArn: !Join [ '', [ 'arn:aws:execute-api:', !Ref 'AWS::Region', ':', !Ref 'AWS::AccountId', ':', !Ref 'TestApi', '/*/*' ] ]
  TestApiStage:
    Type: AWS::ApiGateway::Stage
    Properties:
      DeploymentId: !Ref TestApiDeployment
      MethodSettings:
        - DataTraceEnabled: true
          HttpMethod: '*'
          ResourcePath: /*
      RestApiId: !Ref TestApi
  TestApiDeployment:
    Type: AWS::ApiGateway::Deployment
    DependsOn:
      - TestRequest
    Properties:
      RestApiId: !Ref TestApi
      StageName: DummyStage
      # Deployment with an Empty Embedded Stage
      # The following instructional text is no longer present in the AWS
      # documentation for AWS::ApiGateway::Deployment StageName and it's not
      # clear if it still applies.
      #
      # "Note This property is required by API Gateway. We recommend that you
      # specify a name using any value (see Examples) and that you don’t use
      # this stage. We recommend not using this stage because it is tied to
      # this deployment, which means you can’t delete one without deleting the
      # other. For example, if you delete this deployment, API Gateway also
      # deletes this stage, which you might want to keep. Instead, use the
      # AWS::ApiGateway::Stage resource to create and associate a stage with
      # this deployment."
  TestResource:
    Type: AWS::ApiGateway::Resource
    Properties:
      RestApiId: !Ref TestApi
      ParentId: !GetAtt TestApi.RootResourceId
      PathPart: '{proxy+}'
  TestRequest:
    DependsOn: TestLambdaPermission
    Type: AWS::ApiGateway::Method
    Properties:
      AuthorizationType: NONE
      HttpMethod: GET
      Integration:
        Type: AWS_PROXY
        # IntegrationHttpMethod is POST regardless of the HttpMethod for this resource
        # https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html#set-up-lambda-proxy-integration-using-cli
        # "For Lambda integrations, you must use the HTTP method of POST for the
        # integration request, according to the specification of the Lambda service
        # action for function invocations."
        IntegrationHttpMethod: POST
        Uri: !Join [ '', [ 'arn:aws:apigateway:', !Ref 'AWS::Region', ':lambda:path/2015-03-31/functions/', !GetAtt 'TestFunction.Arn', '/invocations' ] ]
      ResourceId: !Ref TestResource
      RestApiId: !Ref TestApi
  TestPOSTRequest:
    DependsOn: TestLambdaPermission
    Type: AWS::ApiGateway::Method
    Properties:
      AuthorizationType: NONE
      HttpMethod: POST
      Integration:
        Type: AWS_PROXY
        # https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html#set-up-lambda-proxy-integration-using-cli
        # "For Lambda integrations, you must use the HTTP method of POST for the
        # integration request, according to the specification of the Lambda service
        # action for function invocations."
        IntegrationHttpMethod: POST
        Uri: !Join [ '', [ 'arn:aws:apigateway:', !Ref 'AWS::Region', ':lambda:path/2015-03-31/functions/', !GetAtt 'TestFunction.Arn', '/invocations' ] ]
      ResourceId: !Ref TestResource
      RestApiId: !Ref TestApi
  TestRootRequest:
    # This resource is necessary to get API Gateway to respond to requests for the '/' path
    # Without it API Gateway will respond to requests for '/' with the error
    # {"message":"Missing Authentication Token"}
    # https://stackoverflow.com/q/46578615/168874
    # https://stackoverflow.com/q/52909329/168874
    DependsOn: TestLambdaPermission
    Type: AWS::ApiGateway::Method
    Properties:
      AuthorizationType: NONE
      HttpMethod: GET
      Integration:
        Type: AWS_PROXY
        # IntegrationHttpMethod is POST regardless of the HttpMethod for this resource
        # https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html#set-up-lambda-proxy-integration-using-cli
        # "For Lambda integrations, you must use the HTTP method of POST for the
        # integration request, according to the specification of the Lambda service
        # action for function invocations."
        IntegrationHttpMethod: POST
        Uri: !Join [ '', [ 'arn:aws:apigateway:', !Ref 'AWS::Region', ':lambda:path/2015-03-31/functions/', !GetAtt 'TestFunction.Arn', '/invocations' ] ]
      # ResourceId must use the RootResourceId attribute of the AWS::ApiGateway::RestApi
      # https://stackoverflow.com/a/56121914/168874
      ResourceId: !GetAtt TestApi.RootResourceId
      RestApiId: !Ref TestApi
  TestCloudFrontDistribution:
    Type: AWS::CloudFront::Distribution
    Properties:
      DistributionConfig:
        Comment: !Join [ ':', [!Ref 'AWS::StackName', 'Test']]
        DefaultCacheBehavior:
          AllowedMethods:
            - GET
            - HEAD
            - POST
            - DELETE
            - OPTIONS
            - PUT
            - PATCH
          Compress: true
          DefaultTTL: 0
          MinTTL: 0
          MaxTTL: 0
          ForwardedValues:
            Cookies:
              Forward: all
            QueryString: true
            Headers:
              - '*'
          TargetOriginId: TestCloudFrontOriginId
          ViewerProtocolPolicy: redirect-to-https
        # https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloudfront-distribution-distributionconfig.html#cfn-cloudfront-distribution-distributionconfig-defaultrootobject
        DefaultRootObject: ''  # "If you don't want to specify a default root object when you create a distribution, include an empty DefaultRootObject element."
        Enabled: true
        Aliases:
          - !Ref CustomDomainName
        HttpVersion: http2
        IPV6Enabled: true
        #Logging:
        #  Logging
        Origins:
          - CustomOriginConfig:
              OriginProtocolPolicy: https-only
              OriginSSLProtocols:
                - TLSv1.2
            DomainName: !GetAtt TestApiGatewayDomainName.RegionalDomainName
            Id: TestCloudFrontOriginId
            # OriginPath: !Join [ '', [ '/', !Ref 'TestApiStage' ] ]
        PriceClass: PriceClass_100  # US, Canada, Europe, Israel
        ViewerCertificate:
          AcmCertificateArn: !Ref CertificateArn
          MinimumProtocolVersion: TLSv1.2_2018
          SslSupportMethod: sni-only

Если AWS обновит эту страницу документации, чтобы исправить это, вы можете увидеть исходную страницу здесь

...