AWS API Deployment - amazon-web-services

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

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.

AWS Cloudformation: "Invalid permissions on Lambda function" What's wrong?

I'm trying to invoke the lambda ReplySms through the Api Gateway Trigger
This is my template.yaml:
Resources:
SmsRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub ${Environment}-${Application}-role
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Action:
- sts:AssumeRole
Principal:
Service:
- lambda.amazonaws.com
- apigateway.amazonaws.com
Effect: Allow
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
- arn:aws:iam::aws:policy/SecretsManagerReadWrite
SmsApi:
Type: AWS::Serverless::Api
Tags:
username: !Ref UserName
Application: !Ref Application
Properties:
Name: !Sub ${Environment}-sms-service
StageName: !Ref Environment
MethodSettings:
- LoggingLevel: INFO
DataTraceEnabled: false
ResourcePath: "/*"
HttpMethod: "*"
ReplySms:
Type: AWS::Serverless::Function
Properties:
ReservedConcurrentExecutions: 100
FunctionName: !Sub ${Environment}-${Application}-reply-sms
CodeUri: target
Handler: replySms.handler
Runtime: nodejs12.x
MemorySize: 128
Timeout: 30
Role: !GetAtt SmsRole.Arn
ReplySmsResource:
Type: AWS::ApiGateway::Resource
Properties:
RestApiId: !Ref SmsApi
ParentId: !GetAtt SmsApi.RootResourceId
PathPart: replysms
EmptyModel:
Type: AWS::ApiGateway::Model
Properties:
RestApiId: !Ref SmsApi
ContentType: application/xml
Description: Empty schema to map lambda response
Name: EmptyModel
Schema: {}
ReplyMethod:
Type: AWS::ApiGateway::Method
Properties:
AuthorizationType: NONE
HttpMethod: POST
Integration:
Type: AWS
Credentials: !GetAtt SmsRole.Arn
IntegrationHttpMethod: POST
Uri: !Sub 'arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ReplySms.Arn}/invocations'
PassthroughBehavior: WHEN_NO_TEMPLATES
RequestTemplates:
application/x-www-form-urlencoded: |
#set($httpPost = $input.path('$').split("&"))
{
#foreach( $kvPair in $httpPost )
#set($kvTokenised = $kvPair.split("="))
#if( $kvTokenised.size() > 1 )
"$kvTokenised[0]" : "$kvTokenised[1]"#if( $foreach.hasNext ),#end
#else
"$kvTokenised[0]" : ""#if( $foreach.hasNext ),#end
#end
#end
}
IntegrationResponses:
- StatusCode: 200
ResponseTemplates: {"application/xml": "$input.path('$')"}
MethodResponses:
- StatusCode: 200
ResponseModels:
application/xml: !Ref EmptyModel
ResourceId: !Ref ReplySmsResource
RestApiId: !Ref SmsApi
InvokeReplySmsLambda:
Type: AWS::Lambda::Permission
Properties:
Action: lambda:InvokeFunction
FunctionName: !GetAtt ReplySms.Arn
Principal: apigateway.amazonaws.com
SourceArn: !Sub "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${SmsApi}/*/POST/replysms"
# SmsApiDeployment:
# Type: "AWS::ApiGateway::Deployment"
# DependsOn:
# - ReplyMethod
# Properties:
# RestApiId: !Ref SmsApi
# StageName: !Ref Environment
Outputs:
ApiResourceUrl:
Value: !Sub https://${SmsApi}.execute-api.${AWS::Region}.amazonaws.com/${Environment}/
I've been on this since 3 days and still cannot figure out what's wrong. From the other answers I figured out I need to add a Permission resource so I added InvokeReplySmsLambda which should have fixed the error.
But still getting Execution failed due to configuration error: Invalid permissions on Lambda function
(66c44450-af76-4c93-accd-xxxxxxxxxxxx) Extended Request Id: RKHI5FEMIAMFc-w=
(66c44450-af76-4c93-accd-b659d3c8f2ee) Verifying Usage Plan for request: 66c44450-af76-4c93-accd-b659d3c8f2ee. API Key: API Stage: dhrcl2tzqa/sandbox
(66c44450-af76-4c93-accd-b659d3c8f2ee) API Key authorized because method 'POST /replysms' does not require API Key. Request will not contribute to throttle or quota limits
(66c44450-af76-4c93-accd-b659d3c8f2ee) Usage Plan check succeeded for API Key and API Stage dhrcl2tzqa/sandbox
(66c44450-af76-4c93-accd-b659d3c8f2ee) Starting execution for request: 66c44450-af76-4c93-accd-b659d3c8f2ee
(66c44450-af76-4c93-accd-b659d3c8f2ee) HTTP Method: POST, Resource Path: /replysms
(66c44450-af76-4c93-accd-b659d3c8f2ee) Execution failed due to configuration error: Invalid permissions on Lambda function
(66c44450-af76-4c93-accd-b659d3c8f2ee) Method completed with status: 500
Any help would be appreciated.
First,
Remove your InvokeReplySmsLambda AWS::Lambda::Permission resource. You won't need it as SAM CLI will create a policy that can invoke your Lambda implicitly (docs reference).
Should you need the Arn of the implicitly created role it is always: FunctionNameRole.Arn (with usage like !GetAtt ReplySmsRole.Arn in yaml). You can confirm this value in one of the resources tabs of the stack details (cloudformation) or Roles section (IAM) of the aws console.
Second,
Edit your Lambda function to
remove the Role property
Include a Policies property with the the extra policies you want (SecretsManagerReadWrite) .
ReplySms:
Type: AWS::Serverless::Function
Properties:
ReservedConcurrentExecutions: 100
FunctionName: !Sub ${Environment}-${Application}-reply-sms
CodeUri: target
Handler: replySms.handler
Runtime: nodejs12.x
MemorySize: 128
Timeout: 30
Policies:
- SecretsManagerReadWrite
Notes :
I'll also mention you could use the Events (EventSource)'s property of API type and merge away the AWS::ApiGateway::Method resource. An example can be found here :
https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-resource-function.html#sam-resource-function--examples
hths!

Why does CloudFormation API Gateway fail stating 'The REST API doesn't contain any methods' even with DependsOn?

I have a CloudFormation template which I'm trying to deploy. This template is very similar to another template I have which works without issue however when deploying this template it keeps failing stating:
APIGatewayDeployment5332c373d4 CREATE_FAILED The REST API doesn't contain any methods (Service: AmazonApiGateway; Status Code: 400; Error Code: BadRequestException; Request ID: 02c0352f-b23b-44d5-a0af-2f3f3ecd7273)
I had this issue previously on my other template and added the DependsOn property on the deployment which fixed the issue however even with this property this template is failing.
See cut down version of my template below:
AWSTemplateFormatVersion: 2010-09-09
Transform: AWS::Serverless-2016-10-31
Globals:
Function:
Environment:
Variables:
FOO: bah
Api:
EndpointConfiguration: REGIONAL
Resources:
APIGateway:
Type: AWS::Serverless::Api
Properties:
Name: Some API
StageName: !Sub ${EnvironmentTagName}
ApiFooResource:
Type: AWS::ApiGateway::Resource
Properties:
ParentId: !GetAtt APIGateway.RootResourceId
PathPart: foo
RestApiId: !Ref APIGateway
ApiFooNotificationResource:
Type: AWS::ApiGateway::Resource
Properties:
ParentId: !Ref ApiFooResource
PathPart: notification
RestApiId: !Ref APIGateway
ApiFooNotificationMethod:
Type: AWS::ApiGateway::Method
Properties:
HttpMethod: POST
AuthorizationType: NONE
ResourceId: !Ref ApiFooNotificationResource
RestApiId: !Ref APIGateway
MethodResponses:
- StatusCode: '200'
Integration:
Credentials: !GetAtt SQSRole.Arn
IntegrationHttpMethod: POST
IntegrationResponses:
- StatusCode: 200
Type: AWS
Uri: !Sub arn:aws:apigateway:${AWS::Region}:sqs:action/SendMessage
RequestParameters:
integration.request.querystring.QueueUrl: !Sub '''${FooNotificationQueue}'''
integration.request.querystring.MessageBody: method.request.body
ApiTestResource:
Type: AWS::ApiGateway::Resource
Properties:
ParentId: !GetAtt APIGateway.RootResourceId
PathPart: test
RestApiId: !Ref APIGateway
ApiTestMethod:
Type: AWS::ApiGateway::Method
Properties:
HttpMethod: POST
AuthorizationType: NONE
ResourceId: !Ref ApiTestResource
RestApiId: !Ref APIGateway
MethodResponses:
- StatusCode: '200'
Integration:
Credentials: !GetAtt SQSRole.Arn
IntegrationHttpMethod: POST
IntegrationResponses:
- StatusCode: 200
Type: AWS
Uri: !Sub arn:aws:apigateway:${AWS::Region}:sqs:action/SendMessage
RequestParameters:
integration.request.querystring.QueueUrl: !Sub '''${TestQueue}'''
integration.request.querystring.MessageBody: method.request.body
ApiDeployment:
Type: AWS::ApiGateway::Deployment
DependsOn: ApiTestMethod
Properties:
RestApiId: !Ref APIGateway
Description: !Sub Api Deployment for ${EnvironmentTagName}
StageName: !Sub ${EnvironmentTagName}
...**Removed the rest for brevity**...
Parameters:
StackTagName:
Type: String
Description: Stack Name (injected by Stackery at deployment time)
EnvironmentTagName:
Type: String
Description: Environment Name (injected by Stackery at deployment time)

Permissions for Lambda invocation conflicts with API Gateway permissions

I've got an app deployed to Lambda and I'm using API Gateway to forward HTTP requests over to the app.
My problem is that it looks like API Gateway isn't forwarding requests that aren't to the base URL of the app.
In other words, the code inside my Lambda handler gets executed when there's an HTTP request to https://blabblahblah.execute-api.us-west-2.amazonaws.com/Prod/. However, an HTTP request to https://blabblahblah.execute-api.us-west-2.amazonaws.com/Prod/pong throws a 500 and no code gets executed. I've got some logging statements inside my handler and they don't log anything for non-base-URL requests.
I've narrowed the problem down to a permissions issue.
I have 2 sets of permission that aren't working well together.
I need to allow the API Gateway to invoke my lambda function
The lambda function needs to be able to call itself.
I thought I had both these permissions working correctly but any HTTP requests that aren't aimed at the base URL throw a 500 in the API Gateway (i.e. I see no Cloudwatch log entries for the request but the response is 500).
I think that means that there must be some error in my SAM template.
Resources:
IAMRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Statement:
- Action: ['sts:AssumeRole']
Effect: Allow
Principal:
Service: [lambda.amazonaws.com]
Version: 2012-10-17
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
MyFunction:
Type: AWS::Serverless::Function
Properties:
Handler: lambda.handler
Role: !GetAtt IAMRole.Arn
Runtime: ruby2.5
CodeUri: "./src/"
MemorySize: 512
Timeout: 30
Events:
MyAppApi:
Type: Api
Properties:
Path: /
Method: ANY
RestApiId: !Ref MyAppAPI
MyAppAPI:
Type: AWS::Serverless::Api
Properties:
Name: MyAppAPI
StageName: Prod
DefinitionBody:
swagger: '2.0'
basePath: '/'
info:
title: !Ref AWS::StackName
paths:
/{proxy+}:
x-amazon-apigateway-any-method:
responses: {}
x-amazon-apigateway-integration:
uri:
!Sub 'arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyFunction.Arn}/invocations'
passthroughBehavior: "when_no_match"
httpMethod: POST
type: "aws_proxy"
/:
post:
responses: {}
x-amazon-apigateway-integration:
uri:
!Sub 'arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyFunction.Arn}/invocations'
passthroughBehavior: "when_no_match"
httpMethod: POST
type: "aws_proxy"
ConfigLambdaPermission:
Type: "AWS::Lambda::Permission"
DependsOn:
- MyFunction
Properties:
Action: lambda:InvokeFunction
FunctionName: !Ref MyFunction
Principal: apigateway.amazonaws.com
ConfigLambdaPermission:
Type: "AWS::Lambda::Permission"
DependsOn:
- MyFunction
Properties:
Action: lambda:InvokeFunction
FunctionName: !Ref MyFunction
Principal: !GetAtt IAMRole.Arn
Outputs:
MyFunction:
Description: Lambda Function for interacting with Slack
Value:
Fn::GetAtt:
- MyFunction
- Arn
MyAppAppUrl:
Description: App endpoint URL
Value: !Sub "https://${MyAppAPI}.execute-api.${AWS::Region}.amazonaws.com/"
Any idea how I can get things working right?
Yes, you need to add the other resource as well as a event trigger. And you lambda permissions have no use in the above template. Here's how it worked for me.
AWSTemplateFormatVersion: "2010-09-09"
Transform: AWS::Serverless-2016-10-31
Resources:
IAMRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Statement:
- Action: ['sts:AssumeRole']
Effect: Allow
Principal:
Service: [lambda.amazonaws.com]
Version: 2012-10-17
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
MyFunction:
Type: AWS::Serverless::Function
Properties:
Handler: roleTest.roleTest
Role: !GetAtt IAMRole.Arn
Runtime: nodejs8.10
CodeUri: s3://apicfn/roleTest.zip
MemorySize: 512
Timeout: 30
Events:
MyAppApi1:
Type: Api
Properties:
Path: /
Method: ANY
MyAppApi2:
Type: Api
Properties:
Path: /{proxy+}
Method: ANY
Outputs:
MyFunction:
Description: Lambda Function for interacting with Slack
Value:
Fn::GetAtt:
- MyFunction
- Arn
PS: do not even need the Serverless::Api section for this template.

lambda and api gateway defined by cloud formation

I want to add authorization to endpoints used for lambda.
Since I can not set up this in Lambda definition, I create methods explicitly.
LambdaName:
Type: AWS::Serverless::Function
Properties:
CodeUri: ./lambdaDir
FunctionName: LambdaName
Events:
APIEvent:
Type: Api
Properties:
RestApiId: !Ref MyApi
Path: /products
Method: POST
MyApi:
Type: AWS::Serverless::Api
Properties:
Name: MyApi
StageName: dev
ProductsResource:
Type: AWS::ApiGateway::Resource
Properties:
ParentId:
Fn::GetAtt:
- "MyApi"
- "RootResourceId"
PathPart: products
RestApiId: !Ref MyApi
ProductsCreateApiMethod:
Type: AWS::ApiGateway::Method
Properties:
AuthorizationType: NONE
AuthorizerId: !Ref Authorizer
AuthorizationScopes:
- someScope
RestApiId: !Ref MyApi
HttpMethod: POST
ResourceId: !Ref ProductsResource
Unfortunately I get error:
Another resource with the same parent already has this name: products (Service: AmazonApiGateway; Status Code: 409; Error Code: ConflictException; Request ID: 03ca26de-27be-11e9-82bb-536e557773f3)
It seems like when Cloud Formation tries to create explicitly products resource, it is already there created implicitly by lambda definition,