APIGateway Proxy resource doesn't get path parameter into Lambda - amazon-web-services

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.

Related

How to get the Uri for the aws:lambda function as a text?

I'm trying to link an API Gateway to prefined lambda function. However it says unsupported property Uri. My lambda function is created in the given region as "SayHelloFunction".
SayHello:
Type: "AWS::ApiGateway::Method"
DependsOn:
- RootApi
- HelloResource
Properties:
RestApiId: !Ref RootApi
ResourceId: !Ref HelloResource
HttpMethod: PUT
MethodResponses:
- StatusCode: 200
AuthorizationType: NONE
ApiKeyRequired: true
Integration:
IntegrationHttpMethod: PUT
IntegrationResponses:
- StatusCode: 200
Type: AWS_PROXY
Uri: 'arn:aws:apigateway:us-east-2:lambda:path/2015-03-31/functions/arn:aws:lambda:us-east-2:xxxxxxxxxxxx:function:SayHelloFunctionFunctionArn/invocations'
Please help.
You can set using Lambda Arn attribute by calling like below shown.
Uri: !Sub 'arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${SayHelloFunction.Arn}/invocations
You will get more details here : https://github.com/aviboy2006/lambda-apigateway-sns-cnf-awsugblrdemo/blob/main/sample.yaml

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

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

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.

How to create an API Gateway HTTP Proxy without any lambda function

i use the serverless framework and would like to deploy an API Gateway HTTP Proxy but i don't have a Lambda function connected with API Gateway.
I found this in the internet, but this example require one lambda function connected to API Gateway
# ProxyResource:
# Type: AWS::ApiGateway::Resource
# Properties:
# ParentId:
# Fn::GetAtt:
# - ApiGatewayRestApi # our default Rest API logical ID
# - RootResourceId
# PathPart: serverless # the endpoint in your API that is set as proxy
# RestApiId:
# Ref: ApiGatewayRestApi
# ProxyMethod:
# Type: AWS::ApiGateway::Method
# Properties:
# ResourceId:
# Ref: ProxyResource
# RestApiId:
# Ref: ApiGatewayRestApi
# HttpMethod: GET # the method of your proxy. Is it GET or POST or ... ?
# MethodResponses:
# - StatusCode: 200
# Integration:
# IntegrationHttpMethod: POST
# Type: HTTP
# Uri: http://serverless.com # the URL you want to set a proxy to
# IntegrationResponses:
# - StatusCode: 200
If i deploy this i got the error:
The CloudFormation template is invalid: Template format error: Unresolved resource dependencies [ApiGatewayRestApi] in the Resources block of the template
It is possible to deploy just an API Gateway HTTP Proxy ?
Thanks
I figure out how to create an API Gateway if i don't have any lambda function in serverless. I just need to add this to resources and change Ref: ApiGatewayRestApi to Ref: ProxyApi
resources:
Resources:
ProxyApi:
Type: AWS::ApiGateway::RestApi
Properties:
Name: ApiGateway
To fulfill my requirement to use AppSync without any ApiKey - it's possible with these lines:
ProxyApi:
Type: AWS::ApiGateway::RestApi
Properties:
Name: AppSync Graph Proxy
ProxyResource:
Type: AWS::ApiGateway::Resource
Properties:
ParentId:
Fn::GetAtt:
- ProxyApi # our default Rest API logical ID
- RootResourceId
PathPart: graphql # the endpoint in your API that is set as proxy
RestApiId:
Ref: ProxyApi
ProxyMethod:
Type: AWS::ApiGateway::Method
Properties:
ResourceId:
Ref: ProxyResource
RestApiId:
Ref: ProxyApi
AuthorizationType: NONE
HttpMethod: ANY # the method of your proxy. Is it GET or POST or ... ?
MethodResponses:
- StatusCode: 200
Integration:
IntegrationHttpMethod: POST
Type: HTTP
Uri: { Fn::GetAtt: [GraphQlApi, GraphQLUrl] } # the URL you want to set a proxy to
IntegrationResponses:
- StatusCode: 200
RequestParameters:
"integration.request.header.x-api-key": "stageVariables.API_KEY"
ProxyDeployment:
Type: AWS::ApiGateway::Deployment
DependsOn: "ProxyMethod"
Properties:
RestApiId:
Ref: ProxyApi
ProxyStage:
Type: AWS::ApiGateway::Stage
Properties:
StageName: ${opt:stage, self:provider.stage}
RestApiId:
Ref: ProxyApi
DeploymentId:
Ref: ProxyDeployment
Variables:
API_KEY: { Fn::GetAtt: [GraphQlApiKeyDefault, ApiKey] }
For this, you need an working and configured serverless-appsync-plugin in your serverless config