SAM template: Configure an api gateway with multiple lambdas
AWS SAM templates are reasonably fun for deploying serverless workloads if you are not using the serverless framework itself. The default structure of a SAM template with a function and an api gateway works for most use cases. This works wonderfully well to abstract many of the transformation aspects Cloudformation as an IAC (Infrastructure As Code) handles.
The below approach is only for folks who want to use the AWS SAM template and not the serverless framework . I decided to document this as I spent some time as a serverless newbie figuring out how to do IAC ( Infrastructure As Code) using SAM template with my scenario below. The reason I had to do what I have below as an approach is because of the expectation of the SAM template to have the API Gateway reference in the same template. There may be additional approaches available with Serverless::Application resource or a Nested Stack.
At a minimum this is what a SAM template which deploys both a lambda and API Gateway looks like:
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: AWS SAM template
Resources:
LambdaFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: /
Handler: index.handler
Runtime: nodejs12.x
Events:
ProxyApiRoot:
Type: Api
Properties:
Path: /
Method: ANY
The above template creates a lambda function ,an API Gateway resource, necessary lambda execution roles, permissions and associates the lambda with the Api gateway. You can always add all the integration request/response spec in a swagger/openapi yaml and embed it to an API Gateway resource to make your SAM templates do a lot more. But that is for another article.
Now my scenario was a little different; but not uncommon.
- 3 lambda functions in three GitHub repositories.
- All of the lambda functions need to be exposed to specific resources on a single API Gateway.
Easy route :
Move all your lambda functions into a single repo and separate the services out into their own directories. This would be great if there are no dependencies and I do not have to build any of of the dependencies along with templates as part of the sam deployment stages.
root/
├── template.yaml
├── lambda1/
│ ├── index.js
├── lambda2/
│ ├── index.js
└── lambda3/
├── index.js
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: AWS SAM template
Resources:
ApiGatewayApi:
Type: AWS::Serverless::Api
Properties:
StageName: Prod
LambdaFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: /lambda1
Handler: index.handler
Runtime: nodejs12.x
Events:
ProxyApiRoot:
Type: Api
Properties:
RestApiId: !Ref ApiGatewayApi
Path: /lambda1
Method: ANY
LambdaFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: /lambda2
Handler: index.handler
Runtime: nodejs12.x
Events:
ProxyApiRoot:
Type: Api
Properties:
RestApiId: !Ref ApiGatewayApi
Path: /lambda2
Method: ANY
LambdaFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: /lambda3
Handler: index.handler
Runtime: nodejs12.x
Events:
ProxyApiRoot:
Type: Api
Properties:
RestApiId: !Ref ApiGatewayApi
Path: /lambda3
Method: ANY
Second approach:
If you have a rationale to keep the lambda functions in their own git repositories , the below approach should work for you.
- Create the lambda functions in their repos with a SAM template with an output defined for the arn with an export name( repeat for each )
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: AWS SAM template
Resources:
LambdaFunction:
Type: AWS::Serverless::Function
Properties:
- - - #Removed to keep this structure small
Path: /
Method: ANY
Outputs:
LambdaARN:
Description: The ARN of the underlying Lambda function.
Value: !GetAtt LambdaFunction.Arn
Export:
Name: 'LambdaFunctionArn'
- Create a SAM template for the API Gateway and reference the arns in the AWS::Lambda::Permission resource and integration resources in an inline or external swagger yaml.
Transform: AWS::Serverless-2016-10-31
Description: SAM template 2
Resources:
SampleApi:
Type: AWS::Serverless::Api
Properties:
StageName: Prod
DefinitionBody:
swagger: "2.0"
info:
version: "1.0"
title: "ApiSpec"
schemes:
- "https"
paths:
/:
x-amazon-apigateway-any-method:
responses: {}
x-amazon-apigateway-integration:
uri:
!Join
- ''
- - 'arn:aws:apigateway:'
- !Ref 'AWS::Region'
- ':lambda:path/2015-03-31/functions/'
- !ImportValue LambdaFunctionArn
- '/invocations'
passthroughBehavior: "when_no_match"
httpMethod: "POST"
type: "aws_proxy"
LambdaInvocationPermission:
Type: AWS::Lambda::Permission
Properties:
Action: lambda:InvokeFunction
FunctionName:
!ImportValue LambdaFunctionArn
Principal: apigateway.amazonaws.com
SourceArn:
Fn::Sub: "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${SampleApi}/*/*/*"
Things to consider:
- Remember to run the lambda SAM deploys prior to the API Gateway stack deployment in the second approach.
- You can reference one of the lambdas directly in a SAM template along with an API gateway while adding the lambda permission resource for others.
Image Credit : https://github.com/aws/aws-sam-cli