AWS API Gateway - Lambda Proxy not turning on via CloudFormation template - amazon-web-services

I am using CloudFormation to create an API in API Gateway. Each one of my endpoints points to a Lambda function. I need to turn on "Lambda Proxy integration" for each endpoint.
Here's a snippet from my CloudFormation template:
method1:
Type: "AWS::ApiGateway::Method"
Properties:
ApiKeyRequired: true
AuthorizationType: None
HttpMethod: POST
Integration:
Type: AWS_PROXY
IntegrationHttpMethod: POST
IntegrationResponses:
- ResponseTemplates:
application/json: Empty
StatusCode: 200
Uri:
Fn::ImportValue: !Sub '${ProjectName}-${Environment}-method1'
MethodResponses:
- ResponseModels:
application/json: Empty
StatusCode: 200
RequestValidatorId: !Ref validateBodyValidator
ResourceId: !Ref method1Resource
RestApiId: !Ref RestApi
I have set my integration type as AWS_PROXY. When I run this template, everything seems like. I get the following result:
As you see, Use Lambda Proxy integration option is shown as selected.
However, when I make the API call, I get the following error.
[
"Internal Server Error"
]
After a day of trying to find the issue, here's what I found:
If I uncheck the Use Lambda Proxy integration option, re-check it, and deploy - it starts working.
It's almost like - it looks selected but it's not selected. I have to manually uncheck and recheck for EVERY method.
Any thoughts?

I was able to solve this, thanks to congbaoguier for your comment above.
I added the Method1Permission section in the following template where I create my Lambda function:
Method1:
Type: AWS::Lambda::Function
DependsOn:
- Method1Role
- Method1Policy
Properties:
Role: !GetAtt Method1Role.Arn
Code:
S3Bucket: !ImportValue sharedinf-cp-lambdabucketname
S3Key: Method1.jar
Handler: com.companyname.projectname.methodname::handleRequest
Runtime: "java8"
Timeout: "15"
MemorySize: "512"
Method1Permission:
Type: AWS::Lambda::Permission
Properties:
FunctionName: !GetAtt
- Method1
- Arn
Action: 'lambda:InvokeFunction'
Principal: apigateway.amazonaws.com
This allowed API gateway to access my Lambda function.

Related

APIGateway Proxy resource doesn't get path parameter into Lambda

I need to create an API endpoint, which will trigger a Lambda function and return an image from an S3 bucket.
Example URL: https://abc.execute-api.eu-west-1.amazonaws.com/dev/xyz/00/01/23911414.jpeg
I created an APIGateway instance manually using the web console and it’s working fine.
And I created the same (I guess) using CloudFormation and it’s not working.
The Lambda gets triggered, but it doesn't get the path parameter in the event.
But I want the Lambda function to get /xyz/00/01/23911414.jpeg as the event['path'].
Here is a part of my CloudFormation Template:
RestApi:
Type: AWS::ApiGateway::RestApi
Properties:
Description: Example API Gateway
EndpointConfiguration:
Types:
- REGIONAL
Name: imaginary-api
ProxyResource:
Type: AWS::ApiGateway::Resource
Properties:
RestApiId: !Ref RestApi
ParentId: !GetAtt
- RestApi
- RootResourceId
PathPart: '{proxy+}'
ProxyResourceANY:
Type: AWS::ApiGateway::Method
Properties:
RestApiId: !Ref RestApi
ResourceId: !Ref ProxyResource
HttpMethod: ANY
AuthorizationType: NONE
MethodResponses:
- StatusCode: 200
Integration:
Type: AWS
IntegrationHttpMethod: POST
Uri: !Sub arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ImaginaryLambda.Arn}/invocations
Credentials: !GetAtt ApiGatewayIamRole.Arn
PassthroughBehavior: WHEN_NO_TEMPLATES
RequestTemplates:
"image/jpeg": ""
"image/jpg": ""
IntegrationResponses:
- StatusCode: 200
RestAPIDeployment:
Type: AWS::ApiGateway::Deployment
DependsOn:
- ProxyResource
Properties:
RestApiId: !Ref RestApi
StageName: dev
ImaginaryInvoke:
Type: AWS::Lambda::Permission
Properties:
Action: lambda:InvokeFunction
FunctionName: !GetAtt ImaginaryLambda.Arn
Principal: apigateway.amazonaws.com
SourceArn: !Sub arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:abc/dev
This is the first time I'm using APIGateway and I might have done something wrong here. Any help would be highly appreciated.
UPDATE:
Adding RequestParameters to the Method won't work either.
RequestParameters:
method.request.path.proxy: true
You have different possible integrations:
aws (requires data mapping)
aws_proxy
mock
http
http_proxy
The AWS integration type requires data mapping to get the required attributes (check https://docs.aws.amazon.com/apigateway/latest/developerguide/request-response-data-mappings.html).
You can use AWS_PROXY to get the complete event (check: https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html#api-gateway-simple-proxy-for-lambda-input-format) including the path attribute.
You can return binary file (be careful with content length) or return a s3 presigned url (thru a redirection for example).
Let me answer my own question.
I was able to fix this with the following values.
RestApi:
Type: AWS::ApiGateway::RestApi
Properties:
Description: Example API Gateway
BinaryMediaTypes:
- "*/*" # <-- Important
EndpointConfiguration:
Types:
- REGIONAL
Name: imaginary-api
ProxyResourceANY:
Type: AWS::ApiGateway::Method
DependsOn:
ProxyResource
Properties:
RestApiId: !Ref RestApi
ResourceId: !Ref ProxyResource
HttpMethod: ANY
AuthorizationType: NONE
RequestParameters:
method.request.path.proxy: true # <-- Important
MethodResponses:
- StatusCode: 200
Integration:
Type: AWS_PROXY # <-- Important
IntegrationHttpMethod: POST
ContentHandling: CONVERT_TO_TEXT # <-- Important, depends on your code
PassthroughBehavior: WHEN_NO_MATCH
Uri: !Sub arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ImaginaryLambda.Arn}/invocations
Credentials: !GetAtt ApiGatewayIamRole.Arn
Something strange I noticed is, even though the manually created one showed as AWS in the web console, it showed as AWS_PROXY when I exported the stage as a Swagger definition.
Comparing the two Swagger definitions helped me a lot comparing the YAMLs taken out from Export as Swagger + API Gateway Extensions option.

When using HttpApi via SAM is the OpenApi definition the single point of truth?

I've spent the day getting up to speed with SAM and in particular the HttpApi, but have hit a brick wall. Here's what I want to achieve (the dream):
Create a REST API in Open API 3 (the contract)
Pass the api.yaml to HttpApi and have it generate all the boilerplate for the api
Feel a warm glow now that my api docs and my api routes are always the same (as per the contract)
Hand the Open API spec to the frontend guys and the backend guys and have them all nicely meet in the middle
I'm pretty sure that's how it's supposed to work anyway. But here's what is confusing me. I'm adding the integrations to my api.yaml e.g.:
get:
summary: get item
tags:
- Items
responses:
'200':
description: Item returned successfully
content:
application/json:
schema:
$ref: '#/components/schemas/ItemResource'
'404':
$ref: '#/components/responses/NotFound'
x-amazon-apigateway-integration:
payloadFormatVersion: "2.0"
type: "aws_proxy"
httpMethod: "POST"
uri:
Fn::GetAtt [GetItemFunction, Arn]
connectionType: "INTERNET"
Now I was expecting that with this HttpApi would connect the routes in my OpenApi file with the Lambda I'm pointing to in the integration "GetItemFunction". So I could just define my Lambda like so:
GetItemFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: src/
Handler: get-item.handler
Runtime: nodejs12.x
But that doesn't seem to set the routes as expected (I'm testing with "sam local start-api"). Instead to make it work I need to do this:
GetItemFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: src/
Handler: get-item.handler
Runtime: nodejs12.x
Events:
GetAllItems:
Type: HttpApi
Properties:
ApiId: !Ref MyHttpApi
Path: /items/{id}
Method: get
This works, but now I'm defining the routes in the template.yaml file and not the api.yaml file which for my use case defeats the purpose. It also seems to behave in exactly the same way regardless of whether I add the 'x-amazon-apigateway-integration' in the api.yaml or not.
I'm sure I'm missing something here, if someone could set me on the right path it would be greatly appreciated!
When you declare the connection in SAM using the eventsource, then SAM creates the permissions. If you do not use an eventsoursce then SAM does not build the permissions and API gateway does not have rights to invoke the Lambda function. If you want to manually build it then create a role in the SAM template. here is a working example (simplified to one template):
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: OpenAPI test
Resources:
MyHttpApi:
Type: AWS::Serverless::HttpApi
Properties:
DefinitionBody:
openapi: "3.0.1"
info:
title: "OpenAPI test"
paths:
/:
get:
responses:
default:
description: Item returned successfully
x-amazon-apigateway-integration:
payloadFormatVersion: "2.0"
credentials: !GetAtt MyHttpApiRole.Arn
type: "aws_proxy"
httpMethod: "POST"
uri: !GetAtt GetItemFunction.Arn
connectionType: "INTERNET"
x-amazon-apigateway-importexport-version: "1.0"
MyHttpApiRole:
Type: "AWS::IAM::Role"
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: "Allow"
Principal:
Service: "apigateway.amazonaws.com"
Action:
- "sts:AssumeRole"
Policies:
- PolicyName: ApiDirectWriteToSQS
PolicyDocument:
Version: '2012-10-17'
Statement:
Action:
- lambda:InvokeFunction
Effect: Allow
Resource:
- !GetAtt GetItemFunction.Arn
GetItemFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: src/
Handler: app.handler
Runtime: nodejs12.x

Uncheck 'use lambda proxy integration' cloudformation

I have a cloud-formation template to create API Gateway resource.
APIGateWayEQFAPIRequestGET:
DependsOn: LambdaEQFAPIPermission
Type: 'AWS::ApiGateway::Method'
Properties:
AuthorizationType: NONE
HttpMethod: GET
Integration:
Type: AWS
IntegrationHttpMethod: POST
Uri: !Join
- ''
- - 'arn:aws:apigateway:'
- !Ref 'AWS::Region'
- ':lambda:path/2015-03-31/functions/'
- !GetAtt
- LambdaEQFReportsAPIFunction
- Arn
- /invocations
IntegrationResponses:
- StatusCode: 200
ResponseTemplates:
application/json: $input.json('$.body')
RequestTemplates:
application/json: '{}'
ResourceId: !GetAtt
- APIGateWayEQFAPI
- RootResourceId
RestApiId: !Ref APIGateWayEQFAPI
MethodResponses:
- StatusCode: 200
Everything works as expected but "Use Lambda Proxy integration" is checked. I can manually uncheck it, but how do I uncheck using cloudformation.
I tried different Integration.Type: AWS/AWS_PROXY both did not have any impact on it.
I figured out why this is happening.
When I ran the stack I used AWS_PROXY. Then changing AWS_PROXY to AWS did not deploy the method. I had to rename the method and everything looks as expected.

not a valid key=value pair (missing equal-sign) in Authorization header

While hitting an API from Postman I am getting this error.
API details:
URL:
https://account-perf.myglobal.com/v1/users/00uk0khprrME7gZOU0h7/credentials/change_password
Header:
Content-Type:application/json Authorization:Bearer
n7mbkw74jsubd7rauhptdnre
Type:
POST
Body:
{"password":"Baddy125#","token":"eyJhbGci...."}
Edit 1:
Web-service call to generate token-
URL-
https://api-perf.myglobal.com/rest/oauth2/v1/token
Type-
POST
Body-
client_id:abcd client_secret:xyz grant_type:client_credentials
I had this whenever any unhandled endpoint method or resource was called. My setup is an API Gateway with defined resources (e.g. /myendpoint) and defined methods for those endpoints (e.g. GET).
To fix it, I created a Node.js Lambda function that just returned a 404. Then I added any ANY method at the root of the endpoints / and pointed it as a Lambda proxy function to the ANY methods.
Then I added a proxy resource, e.g. /{proxy} -- there's a checkbox you can click when creating a resource to tell it to proxy. An ANY method on that resource pointing to the same Lambda function, deploy the API, and I'm done.
Now instead of the auth bearer token error, I get a proper HTTP 404 error.
#Matt H - That's pretty good idea this gave me an inspiration of another one.
Assuming all of the other paths within the API are explicitly specified, I created a default path /{proxy+} which would return a http 404, message resource not found. Instead of using lambda, I was able to create a mock response, so there isn't even any cost incurred for getting Lambda to return the response.
I created my APIs via Open API spec. This is how my YAML for the implementation would like
/{proxy+}:
x-amazon-apigateway-any-method:
responses:
404:
description: "404 response"
content: {}
x-amazon-apigateway-integration:
responses:
404:
statusCode: "404"
responseTemplates:
application/json: "{\"message\":\"resource not available\"}"
requestTemplates:
application/json: "{\"statusCode\": 404}"
passthroughBehavior: "when_no_templates"
type: "mock"
Serverless also has the ability to specify inline mock response. Below can be a sample:
functions:
default:
handler: handler.default
events:
- http:
path: hello
cors: true
method: get
integration: mock
request:
template:
application/json: '{"statusCode": 404}'
response:
template: $input.path('$')
statusCodes:
404:
pattern: '' #default method
template:
application/json: '{"statusCode": 404, "message":"resource not found"}'
serverless doc: https://www.serverless.com/framework/docs/providers/aws/events/apigateway/#custom-response-templates
Analyze and validate the request path, in case the request is incorrect this error is thrown at API Gateway. I stepped on the same error, when corrected the request parameters it worked well.
Let me know.
I managed to do it by proxying the ANY /{proxy+} route to a lambda which will always respond with an HTTP 404
Since all other routes are precisely configured, this ANY /{proxy+} route acts as the default one and will catch any unmatched request
Here is how I did it with CloudFormation :
Parameters:
RestAPI:
Type: String
RestApiRootResourceId:
Type: String
LambdaName:
Type: String
Path:
Type: String
RootResource:
Type: AWS::ApiGateway::Resource
Properties:
RestApiId: !Ref RestAPI
ParentId: !Ref RestApiRootResourceId
PathPart: !Ref Path
ProxyResource:
Type: 'AWS::ApiGateway::Resource'
Properties:
RestApiId: !Ref RestAPI
ParentId: !Ref RestApiRootResourceId
PathPart: "{proxy+}"
AnyMethod:
Type: 'AWS::ApiGateway::Method'
Properties:
RestApiId: !Ref RestAPI
ResourceId: !Ref ProxyResource
HttpMethod: ANY
Integration:
IntegrationHttpMethod: POST
Type: AWS_PROXY
PassthroughBehavior: WHEN_NO_MATCH
Uri:
Fn::Join:
- ":"
- - !Sub "arn:aws:apigateway:${AWS::Region}:lambda"
- !Sub "path/2015-03-31/functions/arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function"
- !Sub "${LambdaName}/invocations"
ApiGatewayInvokeLambdaPermissionAny:
Type: AWS::Lambda::Permission
Properties:
Action: lambda:InvokeFunction
FunctionName:
Fn::Join:
- ":"
- - !Sub "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function"
- !Ref LambdaName
Principal: apigateway.amazonaws.com
SourceArn:
Fn::Join:
- ":"
- - !Sub "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}"
- !Sub "${RestAPI}/*/ANY/*"
Paramters :
RestAPI is your API ID
RestApiRootResourceId is the ID of the root resource (!GetAtt "RestApi.RootResourceId")
LambdaName is the name of your proxy lambda
Path is something I have bewteen the stage and the root (for mty specific usage)

AWS API Deployment

I have created a cloudformation template to create api gateway/resource/method/function on AWS platform, and also associated a lambda function to my api. Once I create the stack I get a url to hit my aws api. Whenever I hit this url, I get internal server error.
I am not sure what could be the reason, but if I toggle between 2 lambda functions from aws console for integration request and deploy, it starts working as expected.
I don't want to do it manually as deployment should be done at the time of stack creation.
Below is the template I used to create resources
Resources:
RestApi:
Type: AWS::ApiGateway::RestApi
Properties:
Name: !Sub 'testing'
EndpointConfiguration:
Types:
- 'EDGE'
ApigwResource:
Type: AWS::ApiGateway::Resource
Properties:
RestApiId: !Ref RestApi
ParentId: !GetAtt RestApi.RootResourceId
# lambda function
LambdaFunction:
Type: AWS::Lambda::Function
Properties:
Role: 'lambda_role'
Handler: lambda_s3.lambda_handler
Code:
S3Bucket: { 'Fn::ImportValue': !Sub '${S3Bucket}-S3AppsBucketId' }
S3Key: 'lambda_source_code'
Runtime: python3.7
MemorySize: 128
Timeout: 60
FunctionName: !Sub 'lambda_function'
ApiGatewayMethod:
Type: AWS::ApiGateway::Method
Properties:
HttpMethod: PUT
ResourceId: !Ref ApigwResource
RestApiId: !Ref RestApi
AuthorizationType: AWS_IAM
MethodResponses:
- ResponseModels: { 'application/json' : 'Empty' }
StatusCode: 200
Integration:
Type: AWS
IntegrationHttpMethod: PUT
IntegrationResponses:
- StatusCode: 200
Uri: !Sub
- 'arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${lambdaArn}/invocations'
- lambdaArn: !GetAtt LambdaFunction.Arn
ApiGatewayDeployment:
Type: AWS::ApiGateway::Deployment
DependsOn:
- 'ApiGatewayMethod'
Properties:
RestApiId: !Ref RestApi
StageName: !Ref Environment
I found the problem, my api gateway didn't have permission to invoke lambda function. When I was toggling lambda functions manually, it used to provide necessary permissions to my api gateway thus this work around was working. I added below piece to my cloudformation template to make this work.
ConfigLambdaPermission:
Type: "AWS::Lambda::Permission"
DependsOn:
- RestApi
- LambdaFunction
Properties:
Action: lambda:InvokeFunction
FunctionName: !Ref LambdaFunction
Principal: apigateway.amazonaws.com