Reduced TPS using AWS API Gateway - amazon-web-services

We are conducting several performance tests in our integration environment. Our integration is depicted like this:
AWS API Gateway -> AWS App Load Balancer -> AWS ECS (docker) x 3 (using AutoScaling)
Our initial results show:
TPS = 198, 95% Response Time = 442 ms, Requests = 61790 during 5 minutes performance tests.
When we skip the API Gateway and send all the load to the Application Load Balancer we got:
TPS = 901, 95% Response Time = 433 ms, Requests = 280399 during 5 minutes performance tests.
We have disabled the throttling option in the API Gateway but there's no difference. How can I configure the AWS API Gateway in order to achieve more TPS?
Next you can find CFN code that uses Lambda instead of AWS ECS but suffers from the same TPS issue:
deploy.sh
#!/bin/bash
set -ex
AWS_REGION="us-east-1"
CLOUD_FORMATION_STACK_NAME="APILowTPSTest"
CLOUD_FORMATION_TEMPLATE_FILE="service.yaml"
CLOUD_FORMATION_CAPABILITIES="CAPABILITY_NAMED_IAM"
aws --region "${AWS_REGION}" cloudformation deploy \
--template-file "${CLOUD_FORMATION_TEMPLATE_FILE}" \
--stack-name "${CLOUD_FORMATION_STACK_NAME}" \
--capabilities "${CLOUD_FORMATION_CAPABILITIES}" \
--no-fail-on-empty-changeset \
--tags \
Name="${CLOUD_FORMATION_STACK_NAME}-Stack" \
--parameter-overrides \
EnvironmentName="${CLOUD_FORMATION_STACK_NAME}" \
LambdaFunctionName="API-Tps-Mock-Test"
service.yaml
AWSTemplateFormatVersion: 2010-09-09
Description: >
This template deploys a simple environment using an API Gateway and a Lambda Function for Performances tests purposes
Parameters:
EnvironmentName:
Description: Environment Name (Used to name some resources)
Type: String
LambdaFunctionName:
Description: Lambda function that works as mock
Type: String
Resources:
LambdaIAMRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Action:
- sts:AssumeRole
Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Policies:
- PolicyDocument:
Version: 2012-10-17
Statement:
- Action:
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:PutLogEvents
Effect: Allow
Resource:
- !Sub arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/${LambdaFunctionName}:*
PolicyName: lambda
LambdaFunction:
Type: AWS::Lambda::Function
Properties:
Description: Authorization Lambda Function for NCR
FunctionName: !Ref LambdaFunctionName
Handler: index.handler
MemorySize: 128
Timeout: 3
Role: !GetAtt LambdaIAMRole.Arn
Runtime: nodejs14.x
Code:
ZipFile: |
exports.handler = (event, context) => {
return new Promise((resolve) => resolve({
isBase64Encoded: false,
body: JSON.stringify({ Result: "All Good!" }),
statusCode: 200,
}));
}
ApiGateway:
Type: AWS::ApiGateway::RestApi
Properties:
Name: !Join [ "-", [ !Ref EnvironmentName, "API" ] ]
Description: !Join [ "-", [ !Ref EnvironmentName, "API Gateway deployed to test TPS" ] ]
EndpointConfiguration:
Types:
- REGIONAL
Tags:
- Key: Name
Value: !Join [ "-", [ "API", !Ref EnvironmentName ] ]
ApiLambdaPermission:
Type: AWS::Lambda::Permission
Properties:
FunctionName: !Ref LambdaFunction
Action: lambda:InvokeFunction
Principal: apigateway.amazonaws.com
SourceArn:
!Join
- ''
- - 'arn:aws:execute-api:'
- !Ref AWS::Region
- ':'
- !Ref AWS::AccountId
- ':'
- !Ref ApiGateway
- '/*/*/*'
SubRootResource:
Type: 'AWS::ApiGateway::Resource'
Properties:
RestApiId: !Ref ApiGateway
ParentId: !GetAtt ApiGateway.RootResourceId
PathPart: "v1"
SingleRequestResource:
Type: 'AWS::ApiGateway::Resource'
Properties:
RestApiId: !Ref ApiGateway
ParentId: !Ref SubRootResource
PathPart: "test"
ApiGatewaySingleRequestMethod:
Type: AWS::ApiGateway::Method
Properties:
ResourceId: !Ref SingleRequestResource
RestApiId: !Ref ApiGateway
AuthorizationType: "NONE"
HttpMethod: "POST"
Integration:
Type: AWS_PROXY
IntegrationHttpMethod: "POST"
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:
- ApiGatewaySingleRequestMethod
Properties:
RestApiId: !Ref ApiGateway
APIStage:
Type: AWS::ApiGateway::Stage
Properties:
DeploymentId: !Ref ApiGatewayDeployment
MethodSettings:
- HttpMethod: '*'
LoggingLevel: INFO
ResourcePath: /*
DataTraceEnabled: true
RestApiId: !Ref ApiGateway
StageName: 'dev'

Related

AWS Cloudformation Lambda + API Gateway V2: Unable to deploy API because no routes exist in this API

I'm trying to create a API Gateway HTTP API + Lambda integration using CloudFormation, but I'm stuck on this error:
Unable to deploy API because no routes exist in this API (Service: AmazonApiGatewayV2; Status Code: 400; Error Code: BadRequestException; Request ID: f606986f-d3e6-4dfd-bc20-77011b15a3f9; Proxy: null)
Here's my CloudFormation template:
AWSTemplateFormatVersion: 2010-09-09
Resources:
LambdaRole:
Type: AWS::IAM::Role
Properties:
Policies:
- PolicyName: LambdaPolicy
PolicyDocument:
Version: 2012-10-17
Statement:
- Action:
- 'logs:CreateLogGroup'
- 'logs:CreateLogStream'
- 'logs:PutLogEvents'
Resource:
- 'arn:aws:logs:*:*:*'
Effect: Allow
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Action:
- sts:AssumeRole
Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Lambda:
Type: AWS::Lambda::Function
Properties:
FunctionName: 'getFruit'
Role: !GetAtt LambdaRole.Arn
Handler: index.handler
Runtime: nodejs16.x
MemorySize: 128
Code:
ZipFile: |
exports.handler = async (event) => {
const response = {
body: JSON.stringify([
{ id: 1, name: 'banana', price: 1 },
{ id: 2, name: 'pineapple', price: 2 },
]),
statusCode: 200
}
return response
}
LambdaInvokePermission:
Type: AWS::Lambda::Permission
Properties:
FunctionName: !Ref Lambda
Action: "lambda:InvokeFunction"
Principal: apigateway.amazonaws.com
LambdaIntegration:
Type: AWS::ApiGatewayV2::Integration
Properties:
ApiId: !Ref MyApiGateway
Description: Lambda proxy integration
IntegrationType: AWS_PROXY
IntegrationMethod: POST
PayloadFormatVersion: "2.0"
IntegrationUri: !Sub 'arn:${AWS::Partition}:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${Lambda.Arn}/invocations'
MyApiGateway:
Type: AWS::ApiGatewayV2::Api
Properties:
Name: "MyApiGateway"
ProtocolType: "HTTP"
MyApiGatewayStage:
Type: AWS::ApiGatewayV2::Stage
Properties:
AutoDeploy: true
DeploymentId: !Ref MyApiGatewayDeployment
StageName: '$default'
ApiId: !Ref MyApiGateway
MyApiGatewayDeployment:
Type: AWS::ApiGatewayV2::Deployment
Properties:
ApiId: !Ref MyApiGateway
MyApiRoute:
Type: AWS::ApiGatewayV2::Route
Properties:
ApiId: !Ref MyApiGateway
RouteKey: "GET /"
AuthorizationType: NONE
Target: !Join
- /
- - integrations
- !Ref LambdaIntegration
Try adding a DependsOn properties to the deployment for the routes you create.
MyApiGatewayDeployment:
Type: AWS::ApiGatewayV2::Deployment
DependsOn:
- MyApiRoute
Properties:
ApiId: !Ref MyApiGateway

Lambda permission not being created when using cloudformation

I am trying to create a simple infra using cloudformation. I have to create an Rest API Gateway and Lambda function. That function will be called using API Gateway.
Api Gateway > Lambda
The cloudformation code is below ( I am not showing the code related to role creation or managed policy).
medtestFunction:
Type: "AWS::Lambda::Function"
Properties:
Description: ""
Environment:
Variables:
APIID: !Ref medTestRestapi
SLACK_VERIFICATION_TOKEN:
Ref: SlackVerificationToken
SLACK_INCOMING_WEBHOOK_URL:
Ref: SlackIncomingWebhookURL
FunctionName: "med-test2"
Handler: "index.handler"
Architectures:
- "x86_64"
Code:
S3Bucket:
Ref: S3CodeBucket
S3Key:
Ref: MedTestFunctionS3Key
MemorySize: 128
Role: !GetAtt medtestrole.Arn
Runtime: "nodejs14.x"
Timeout: 6
TracingConfig:
Mode: "PassThrough"
medTestRestapi:
Type: "AWS::ApiGateway::RestApi"
Properties:
Name: "medtest2"
Description: "medtest2"
ApiKeySourceType: "HEADER"
EndpointConfiguration:
Types:
medTestApiStage:
Type: "AWS::ApiGateway::Stage"
Properties:
StageName: "a"
DeploymentId: !Ref medTestApiDeployment
RestApiId: !Ref medTestRestapi
Description: "a"
CacheClusterEnabled: false
TracingEnabled: false
medTestApiMethod:
DependsOn: medtestFunction
Type: "AWS::ApiGateway::Method"
Properties:
RestApiId: !Ref medTestRestapi
ResourceId: !GetAtt medTestRestapi.RootResourceId
HttpMethod: "POST"
AuthorizationType: "NONE"
ApiKeyRequired: false
RequestParameters: {}
MethodResponses:
-
ResponseModels:
"application/json": "Empty"
StatusCode: "200"
Integration:
ContentHandling: "CONVERT_TO_TEXT"
IntegrationHttpMethod: "POST"
IntegrationResponses:
-
ResponseTemplates: {}
StatusCode: "200"
PassthroughBehavior: "WHEN_NO_MATCH"
TimeoutInMillis: 29000
Type: "AWS_PROXY"
Uri: !Sub "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:med-test2/invocations"
medTestApiDeployment:
DependsOn: medTestApiMethod
Type: "AWS::ApiGateway::Deployment"
Properties:
RestApiId: !Ref medTestRestapi
Description: "a"
medTestFunctionPermission:
DependsOn: [medTestApiDeployment, medTestApiMethod]
Type: "AWS::Lambda::Permission"
Properties:
Action: "lambda:InvokeFunction"
FunctionName: !GetAtt medtestFunction.Arn
Principal: "apigateway.amazonaws.com"
SourceArn: !Join [ ":", ["arn:aws:execute-api", !Ref AWS::Region, !Ref AWS::AccountId, !Ref medTestRestapi, "/*/POST/" ] ]
After stack creation when I am checking the function it says
The API with ID : could not be found.
But when I add the trigger manually using console on top of the created stack then it works. Any idea what I am doing wrong? Thanks
you've got one colon too many in SourceArn of medTestFunctionPermission
The one just after the API Gateway ID
You have:
arn:aws:execute-api:eu-west-1:<accountId>:<apiGWId>:/*/POST/
Should be:
arn:aws:execute-api:eu-west-1:<accountId>:<apiGWId>/*/POST/
You can use !Sub instead of !Join. It's easier to read:
SourceArn: !Sub arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${medTestRestapi}/*/POST/

AWS Lambda function is missing trigger by websocket gateway when using CloudFormation

I am trying to set-up a websocket gateway to a Lambda function in AWS. When I do this manually I can successfully deploy the websocket and try it out using wscat. However I would like to build the architecture up using CloudFormation.
The structure of my CloudFormation yaml file looks like this:
AWSTemplateFormatVersion: '2010-09-09'
Parameters:
lambdaRole:
Type: String
Default: ...
backendRole:
Type: String
Default: ...
lambdaImage:
Type: String
Default: ...
Resources:
MyLambdaFunction:
Type: AWS::Lambda::Function
Properties:
Code:
ImageUri: !Sub ${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/${backendRole}:${lambdaImage}
Description: lambda connect function
FunctionName: myLambdaFunction
MemorySize: 128
Role: !Sub arn:aws:iam::${AWS::AccountId}:role/${lambdaRole}
Timeout: 3
PackageType: Image
MyWebSocket:
Type: AWS::ApiGatewayV2::Api
Properties:
Name: MyWebSocket
ProtocolType: WEBSOCKET
RouteSelectionExpression: $request.body.action
MyIntegration:
Type: AWS::ApiGatewayV2::Integration
Properties:
ApiId: !Ref MyWebSocket
Description: Lambda Integration
IntegrationType: AWS_PROXY
IntegrationUri: !Join
- ''
- - 'arn:'
- !Ref 'AWS::Partition'
- ':apigateway:'
- !Ref 'AWS::Region'
- ':lambda:path/2015-03-31/functions/'
- !GetAtt MyLambdaFunction.Arn
- /invocations
IntegrationMethod: POST
MyConnectRoute:
Type: AWS::ApiGatewayV2::Route
Properties:
ApiId: !Ref MyWebSocket
RouteKey: $connect
Target: !Join
- /
- - integrations
- !Ref MyIntegration
MyDefaultRoute:
Type: AWS::ApiGatewayV2::Route
Properties:
ApiId: !Ref MyWebSocket
RouteKey: $default
Target: !Join
- /
- - integrations
- !Ref MyIntegration
MyResponseRoute:
Type: AWS::ApiGatewayV2::Route
Properties:
ApiId: !Ref MyWebSocket
RouteKey: add
RouteResponseSelectionExpression: $default
Target: !Join
- /
- - integrations
- !Ref MyIntegration
MyRouteResponse:
Type: AWS::ApiGatewayV2::RouteResponse
Properties:
RouteId: !Ref MyResponseRoute
ApiId: !Ref MyWebSocket
RouteResponseKey: $default
MyIntegrationResponse:
Type: AWS::ApiGatewayV2::IntegrationResponse
Properties:
IntegrationId: !Ref MyIntegration
IntegrationResponseKey: /201/
ApiId: !Ref MyWebSocket
testStage:
Type: AWS::ApiGatewayV2::Stage
DependsOn:
- MyConnectRoute
- MyDefaultRoute
- MyResponseRoute
Properties:
ApiId: !Ref MyWebSocket
StageName: testStage
MyDeployment:
Type: AWS::ApiGatewayV2::Deployment
Properties:
ApiId: !Ref MyWebSocket
Description: My Deployment
StageName: !Ref testStage
The stack is build without any errors and (almost) everything looks like I intended. However other than in the manually build version the integration of the Lambda function into the websocket does not seem to add the required trigger for the Lambda function. When I manually add a Lambda function to an API Gateway, this automatically adds the trigger.
What do I need to change in my CloudFormation Yaml file to also add the trigger to the Lambda function?
Trigger automatically added to Lambda function when Lambda function is manually added to API Gateway:
No trigger added when Lambda function is added to API Gateway using CloudFormation:
It looks like you may just need to include the Lambda permission for api gateway.
LambdaFunctionPermission:
Type: "AWS::Lambda::Permission"
Properties:
Action: "lambda:InvokeFunction"
Principal: apigateway.amazonaws.com
FunctionName: !Ref MyLambdaFunction
DependsOn: MyWebSocket
If you are having issues with Cloudformation creation it would probably be helpful to also create an account config for logging and chain the dependsOn order of the gateway resources like this.
ApiGwAccountConfig:
Type: "AWS::ApiGateway::Account"
Properties:
CloudWatchRoleArn: !GetAtt "rRoleForCloudtrail.Arn"
rRoleForCloudtrail:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- apigateway.amazonaws.com
Action:
- sts:AssumeRole
Policies:
-
PolicyName: "apigateway-cloudwatch"
PolicyDocument:
Version: "2012-10-17"
Statement:
-
Effect: "Allow"
Action:
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:DescribeLogGroups
- logs:DescribeLogStreams
- logs:PutLogEvents
- logs:GetLogEvents
- logs:FilterLogEvents
Resource: "*"
testStage:
Type: AWS::ApiGatewayV2::Stage
Properties:
ApiId: !Ref MyWebSocket
StageName: testStage
DefaultRouteSettings:
DetailedMetricsEnabled: true
LoggingLevel: INFO
DataTraceEnabled: true
DependsOn: ApiGwAccountConfig
MyDeployment:
Type: AWS::ApiGatewayV2::Deployment
DependsOn:
- MyConnectRoute
- MyDefaultRoute
- MyResponseRoute
Properties:
ApiId: !Ref MyWebSocket
Description: My Deployment
StageName: !Ref testStage
I guess you need AWS::Lambda::Permission resource, e.g.:
LambdaPermission:
Type: AWS::Lambda::Permission
Properties:
Action: lambda:invokeFunction
FunctionName: !GetAtt MyLambdaFunction.Arn
Principal: apigateway.amazonaws.com
SourceArn:
Fn::Join:
- ''
- - 'arn:aws:execute-api:'
- Ref: AWS::Region
- ":"
- Ref: AWS::AccountId
- ":"
- Ref: MyWebSocket
- "/*"
It may need some tweaking - it works with AWS::ApiGateway::RestApi, not sure if there is difference for AWS::ApiGatewayV2::Api.

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!

cloudFormation: api-gateway with nested resources - 500 error during testing workflow

I built an cloudFormation template. It perform a simple workflow: client make a request -> api gateway handle it and send to lambda fucntion via proxy integration. There is a role between api gateway an a lambda.
My template consist of:
root resource (AudienceApi)
nested resource (Segment)
method post (PostMethod) with lambda`s integration
lambda fucntion (lambdaFunction)
lambda invoker (it is a permission for api gateway to invoke the lambda)
lambda role
And during testing the whole workflow I have faced with a problem - api responce with an error 500 Internal Server Error. From log I found message:
Invalid permissions on Lambda function.
I continue testing and find out when I remove nested resource (Segment) and connect method post resource (PostMethod) directly to root resource (AudienceApi) my workflow started to working.
Question: What wrong with my template? why it is not working with nested resource path?
Maybe someone look at my template and find an error?
template:
AWSTemplateFormatVersion: "2010-09-09"
Description: "My API Gateway and Lambda function"
Parameters:
apiGatewayStageName:
Type: "String"
AllowedPattern: "^[a-z0-9]+$"
Default: "call"
lambdaFunctionName:
Type: "String"
AllowedPattern: "^[a-zA-Z0-9]+[a-zA-Z0-9-]+[a-zA-Z0-9]+$"
Default: "my-function"
Resources:
AudienceApi:
Type: "AWS::ApiGateway::RestApi"
Properties:
Name: "my-api"
Description: "My API"
Segment:
Type: 'AWS::ApiGateway::Resource'
Properties:
RestApiId: !Ref AudienceApi
ParentId: !GetAtt
- AudienceApi
- RootResourceId
PathPart: segment
PostMethod:
Type: 'AWS::ApiGateway::Method'
Properties:
HttpMethod: POST
# RequestModels:
# application/json: !Ref SegmentationRequestModel
AuthorizationType: NONE
Integration:
IntegrationHttpMethod: POST
Type: AWS_PROXY
Uri: !Sub
- "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${lambdaArn}/invocations"
- lambdaArn: !GetAtt "lambdaFunction.Arn"
ResourceId: !Ref Segment
RestApiId: !Ref AudienceApi
lambdaFunction:
Type: "AWS::Lambda::Function"
Properties:
Code:
ZipFile: |
def handler(event,context):
return {
'body': 'Hello there {0}'.format(event['requestContext']['identity']['sourceIp']),
'headers': {
'Content-Type': 'text/plain'
},
'statusCode': 200
}
Description: "My function"
FunctionName: !Ref "lambdaFunctionName"
Handler: "index.handler"
MemorySize: 128
Role: !GetAtt "lambdaIAMRole.Arn"
Runtime: "python2.7"
Timeout: 10
lambdaApiGatewayInvoke:
Type: "AWS::Lambda::Permission"
Properties:
Action: "lambda:InvokeFunction"
FunctionName: !GetAtt "lambdaFunction.Arn"
Principal: "apigateway.amazonaws.com"
SourceArn: !Sub "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${AudienceApi}/*/POST/"
lambdaIAMRole:
Type: "AWS::IAM::Role"
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Action:
- "sts:AssumeRole"
Effect: "Allow"
Principal:
Service:
- "lambda.amazonaws.com"
Policies:
- PolicyDocument:
Version: "2012-10-17"
Statement:
- Action:
- "logs:CreateLogGroup"
- "logs:CreateLogStream"
- "logs:PutLogEvents"
Effect: "Allow"
Resource:
- !Sub "arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/${lambdaFunctionName}:*"
PolicyName: "lambda"
lambdaLogGroup:
Type: "AWS::Logs::LogGroup"
Properties:
LogGroupName: !Sub "/aws/lambda/${lambdaFunctionName}"
RetentionInDays: 90
Based on the comments, the solution was to add * to the ARN after the POST/ in SourceArn:
SourceArn: !Sub "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${AudienceApi}/*/POST/*"