0
votes

I am trying to write a cloudformation script that would create a lambda function and hook it up to the API Gateway proxy resource. Stack creation works, but there is something wrong with permissions or integration config, when I test the endpoint, I keep getting

Mon Feb 12 06:45:28 UTC 2018 : Endpoint response body before transformations: Unable to determine service/operation name to be authorized

Mon Feb 12 06:45:28 UTC 2018 : Endpoint response headers: {Connection=keep-alive, x-amzn-RequestId=4fdf1e92-0fc0-11e8-b3f1-0134476f962c, Content-Length=130, Date=Mon, 12 Feb 2018 06:45:28 GMT} Mon Feb 12 06:45:28 UTC 2018 : Execution failed due to configuration error: Malformed Lambda proxy response Mon Feb 12 06:45:28 UTC 2018 : Method completed with status: 502

Here is my cloudformation script:

AWSTemplateFormatVersion: 2010-09-09
Description: An API that proxies requests to another HTTP endpoint

Resources:
  MyFunction:
    Type: 'AWS::Lambda::Function'
    Properties:
      Handler: samplefunction.lambda_handler
      Runtime: python2.7
      Code:
        S3Bucket: "ilya-lambdas"
        S3Key: "lambda-code.zip"
      Role: 'arn:aws:iam::acc-id:role/service-role/basic_lambda_role'


  Api:
    Type: 'AWS::ApiGateway::RestApi'
    Properties:
      Name: foo3

  Resource:
    Type: 'AWS::ApiGateway::Resource'
    Properties:
      ParentId: !GetAtt Api.RootResourceId
      RestApiId: !Ref Api
      PathPart: 'test'


  RootMethod:
    Type: 'AWS::ApiGateway::Method'
    Properties:
      AuthorizationType: NONE
      HttpMethod: ANY
      ResourceId: !GetAtt Api.RootResourceId
      RestApiId: !Ref Api 
      Integration:
          IntegrationHttpMethod: ANY
          IntegrationResponses:
            - StatusCode: 200
              SelectionPattern: .*
          Type: AWS_PROXY
          PassthroughBehavior: WHEN_NO_MATCH
          Uri: !Join ["", ["arn:aws:apigateway:", "us-east-1", ":lambda:path/2015-03-31/functions/", !GetAtt MyFunction.Arn, "/invocations"] ]
          Credentials: 'arn:aws:iam::acc-id:role/service-role/basic_lambda_role'

  ProxyMethod:
      Type: 'AWS::ApiGateway::Method'
      Properties:
        HttpMethod: ANY
        ResourceId: !Ref Resource
        RestApiId: !Ref Api
        AuthorizationType: NONE
        Integration:
          IntegrationHttpMethod: ANY
          IntegrationResponses:
            - StatusCode: 200
              SelectionPattern: .*
          Type: AWS_PROXY
          Uri: !Join ["", ["arn:aws:apigateway:", "us-east-1", ":lambda:path/2015-03-31/functions/", !GetAtt MyFunction.Arn, "/invocations"] ]
          PassthroughBehavior: WHEN_NO_MATCH
          Credentials: 'arn:aws:iam::acc-id:role/service-role/basic_lambda_role'

  FunctionPermissions:
    Type: "AWS::Lambda::Permission"
    Properties: 
      Action: "lambda:InvokeFunction"        
      FunctionName: !GetAtt MyFunction.Arn
      Principal: "apigateway.amazonaws.com"
      SourceArn: !Join [ "", ["arn:aws:execute-api:", !Ref "AWS::Region", ":", !Ref "AWS::AccountId", ":", !Ref Api, "/*/*/*" ] ] 



  Deployment:
    DependsOn:
      - MyFunction
      - RootMethod
      - ProxyMethod
    Type: 'AWS::ApiGateway::Deployment'
    Properties:
      RestApiId: !Ref Api
      StageName: prod

I've been stuck on this for a while now, any pointers will be greatly appreciated.

2

2 Answers

0
votes

Firstly, I notice that your IntegrationHttpMethod is ANY. For Lambdas, unless you're using the {proxy+} configuration, try using POST. I'm pretty sure the CloudFormation docs are still out of date for this, but you'll find some useful info in this answer.

The second thing that I notice is the malformed proxy response, which in your case could just be a misconfiguration. Just to rule this out, dealing with a malformed proxy response is answered on the AWS support centre. Basically, your lambda response should be in the following format, including not adding any extra keys.

{
    "isBase64Encoded": true|false,
    "statusCode": httpStatusCode,
    "headers": { "headerName": "headerValue", ... },
    "body": "..."
}

You could use the example function on the support doc in place of your regular function so that you can isolate the problem.

0
votes

After some trial and error, combined with Miles' advice, I've arrived at the working CloudFormation script:

AWSTemplateFormatVersion: 2010-09-09
Description: An API that proxies requests to another HTTP endpoint

Resources:
  MyFunction:
    Type: 'AWS::Lambda::Function'
    Properties:
      Handler: samplefunction.lambda_handler
      Runtime: python2.7
      Code:
        S3Bucket: "ilya-lambdas"
        S3Key: "lambda-code.zip"
      Role: !Join ["", ["arn:aws:iam::", !Ref "AWS::AccountId", ":role/service-role/basic_lambda_role"] ]


  Api:
    Type: 'AWS::ApiGateway::RestApi'
    Properties:
      Name: foo3

  Resource:
    Type: 'AWS::ApiGateway::Resource'
    Properties:
      ParentId: !GetAtt Api.RootResourceId
      RestApiId: !Ref Api
      PathPart: 'test'


  RootMethod:
    Type: 'AWS::ApiGateway::Method'
    Properties:
      AuthorizationType: NONE
      HttpMethod: ANY
      ResourceId: !GetAtt Api.RootResourceId
      RestApiId: !Ref Api 
      Integration:
          IntegrationHttpMethod: POST
          Type: AWS_PROXY
          PassthroughBehavior: WHEN_NO_MATCH
          Uri: !Join ["", ["arn:aws:apigateway:", "us-east-1", ":lambda:path/2015-03-31/functions/", !GetAtt MyFunction.Arn, "/invocations"] ]

  ProxyMethod:
      Type: 'AWS::ApiGateway::Method'
      Properties:
        HttpMethod: ANY
        ResourceId: !Ref Resource
        RestApiId: !Ref Api
        AuthorizationType: NONE
        Integration:
          IntegrationHttpMethod: POST
          Type: AWS_PROXY
          Uri: !Join ["", ["arn:aws:apigateway:", "us-east-1", ":lambda:path/2015-03-31/functions/", !GetAtt MyFunction.Arn, "/invocations"] ]
          PassthroughBehavior: WHEN_NO_MATCH

  FunctionPermissions:
    Type: "AWS::Lambda::Permission"
    Properties: 
      Action: "lambda:InvokeFunction"        
      FunctionName: !GetAtt MyFunction.Arn
      Principal: "apigateway.amazonaws.com"
      SourceArn: !Join [ "", ["arn:aws:execute-api:", !Ref "AWS::Region", ":", !Ref "AWS::AccountId", ":", !Ref Api, "/*" ] ] 

  Deployment:
    DependsOn:
      - MyFunction
      - RootMethod
      - ProxyMethod
    Type: 'AWS::ApiGateway::Deployment'
    Properties:
      RestApiId: !Ref Api
      StageName: prod

Summary of differences between what I had yesterday (not working), and this (working):

  1. Removed the Credentials object from the Integration sections.
  2. Changed IntegrationHttpMethod from ANY to POST (kudos to Miles for pointing this out)
  3. Under FunctionPermissions changed SourceArn to end with /* instead of /*/*/*

While in this instance response of my lambda function wasn't a problem, it is important that it is formatted correctly. So here is my function, hope having it all in one place will be helpful to folks.

def lambda_handler(event, context):
    response = {
        "isBase64Encoded": "false",
        "statusCode": 200,
        "headers": { "Content-Type": "application/json"},
        "body": "hello from sample function"
    }

    return response