How to use an external layer with the Serverless Framework? - aws-lambda

I would like to use an external layer arn:aws:lambda:eu-central-1:347034527139:layer:tf_keras_pillow:1 in my Serverless project.
I do so by having the following in my serverless.yml:
functions:
api:
handler: functions/api/handler.run
layers: arn:aws:lambda:eu-central-1:347034527139:layer:tf_keras_pillow:1
events:
- http:
path: /image/{id}/{mode}
method: get
request:
parameters:
paths:
id: true
mode: true
However, when checking the AWS Lambda function in the console, there is no layer added after deployment. Any ideas?
The only way to add the layer is by manually doing so in the GUI.

The layers value is an array, per the documentation: https://serverless.com/framework/docs/providers/aws/guide/layers#using-your-layers.
functions:
api:
handler: functions/api/handler.run
layers:
- arn:aws:lambda:eu-central-1:347034527139:layer:tf_keras_pillow:1
events:
- http:
path: /image/{id}/{mode}
method: get
request:
parameters:
paths:
id: true
mode: true
Should work.

Related

How to pass api endpoint parameter to lambda function in serverless

I created a lambda function behind an API gateway, and I am trying to implement a healthcheck for it as well, but right now I need to do this in 2 steps:
run serverless deploy so it spits out the endpoint for the api gateway.
manually insert the endpoint into the healthcheck.environment.api_endpoint param, and run serverless deploy a second time
functions:
app:
handler: wsgi_handler.handler
events:
- http:
method: ANY
path: /
- http:
method: ANY
path: '{proxy+}'
healthcheck:
environment:
api_endpoint: '<how to reference the api endpoint?>'
handler: healthcheck.handler
events:
- schedule: rate(1 minute)
custom:
wsgi:
app: app.app
pythonBin: python3
packRequirements: false
pythonRequirements:
dockerizePip: non-linux
Is there a way to get the reference to the api gateway on creation time, so it can be passed as an environment variable to the healthcheck app? the alternative I can think of, is to basically create a specific serverless.yaml just for the healthcheck purpose.
I noticed I can reconstruct the endpoint in the lambda, and grab the id like so:
healthcheck:
environment:
api_endpoint: !Ref ApiGatewayRestApi
region: ${env:region}
handler: healthcheck.handler
events:
- schedule: rate(1 minute)
and then reconstruct:
def handler(event, context):
api_id = os.environ['api_endpoint']
region = os.environ['region']
endpoint = f"https://{api_id}.execute-api.{region}.amazonaws.com/dev"
With a bit of cloudformation you can inject it directly! That way you don't need to compute it everytime in your lambda handler.
api_endpoint: {
'Fn::Join': [
'',
[
{ Ref: 'WebsocketsApi' },
'.execute-api.',
{ Ref: 'AWS::Region' },
'.',
{ Ref: 'AWS::URLSuffix' },
"/${opt:stage, self:provider.stage, 'dev'}",
],
],
},
(this is an example for a websocket API)
Is your API created through the same/another Cloudformation Stack? If so, you can reference is directly (same stack) or through a CloudFormation variable export.
https://carova.io/snippets/serverless-aws-cloudformation-output-stack-variables
https://carova.io/snippets/serverless-aws-reference-other-cloudformation-stack-variables
If you created it outside of CloudFormation, ie. in the aws console, then you'll need to add the ids into the template. Most likely by creating different environment variables based on the stage.

How dynamically add arn cognito to lambda?

I would like to add the cognito authorizer to my lambda function, but for this I need arn cognito, which is created in the stack coud formation(CognitoUserPool in my resources section). I'm using serverless framework.
part of the serverless.yml file
graphql:
handler: src/lambda-functions/graphql/index.handler
timeout: 30
memorySize: 2048
events:
- http:
path: graphql
method: any
private: true
cors: true
authorizer:
arn:
Fn::Join:
- ''
- - 'arn:aws:cognito-idp:'
- ${self:provider.region}
- ':'
- Ref: AWS::AccountId
- ':userpool/'
- Ref: CognitoUserPool
I am getting an error while deploying the application:
TypeError: functionArn.split is not a function
While debugging, I discovered that the function to which the output from Fn::join should be passed is object:
{"Fn::Join":["",["arn:aws:cognito-idp:","eu-west-1",":",{"Ref":"AWS::AccountId"},":userpool/",{"Ref":"CognitoUserPool"}]]}
And there should be passed to the function already resolve arn for example:
arn:aws:cognito-idp:eu-west-1:XXXXXXXXXX:userpool/eu-west-XXXXXXX
How to force that the output from Fn::join to be computed and pass that value to the arn property?
According to this, giving the authorizer a name lets you use intrinsic functions to refer to the ARN:
graphql:
handler: src/lambda-functions/graphql/index.handler
timeout: 30
memorySize: 2048
events:
- http:
path: graphql
method: any
private: true
cors: true
authorizer:
name: CognitoAuthorizer #supply a unique name here
arn:
!GetAtt CognitoUserPool.Arn

Call service from existing api gateway using base path mappings

Our API has the following endpoints:
POST /users - create a user
GET /users/{userId} - get a particular user
GET /posts/{postId} - get a particular post
GET /posts/{postId}/users - get the users who contributed to this post
I have defined two services: users-service and posts-service. In these two services I define the lambdas like so. I'm using the serverless-domain-manager plugin to create base path mappings:
/users-service/serverless.yaml:
service: users-service
provider:
name: aws
runtime: nodejs10.x
stage: dev
plugins:
- serverless-domain-manager
custom:
customDomain:
domainName: 'serverlesstesting.example.com'
basePath: 'users'
stage: ${self:provider.stage}
createRoute53Record: true
functions:
create:
name: userCreate
handler: src/create.handler
events:
- http:
path: /
method: post
get:
name: userGet
handler: src/get.handler
events:
- http:
path: /{userId}
method: get
/rooms-service/serverless.yaml:
service: posts-service
provider:
name: aws
runtime: nodejs10.x
stage: dev
plugins:
- serverless-domain-manager
custom:
customDomain:
domainName: 'serverlesstesting.example.com'
basePath: 'posts'
stage: ${self:provider.stage}
createRoute53Record: true
functions:
get:
name: postsGet
handler: src/get.handler
events:
- http:
path: /{postId}
method: get
getUsersForPost:
handler: userGet ?
events: ??
The problem is that the GET /posts/{postId}/users actually calls the same userGet lambda from the users-service. But the source for that lambda lives in the users-service, not the posts-service.
So my question becomes:
How do I reference a service from another service using base path mappings? In other words, is it possible for the posts service to actually make a call to the parent custom domain and into the users base path mapping and its service?
Consider or refer below approach
https://serverless-stack.com/chapters/share-an-api-endpoint-between-services.html

How can i use multiple path parameters from serverless framework

I'm trying to deploy my serverless app.
But have a problem like below.
An error occurred: ApiGatewayResourceServicesServiceidVar - A sibling ({id}) of this resource already has a variable path part -- only one is allowed
And below is my code.
updateApplication:
handler: handler.updateApplication
memorySize: 3008
description: Update application
timeout: 30
events:
- http:
path: services/{serviceId}/applications/{applicationId}
method: post
cors: true
authorizer: authorize
request:
parameters:
paths:
serviceId: true
applicationId: true
Any advice or suggestion would be appreciated. Thank you in advance.
The serverless framework seems to be complaining that you have defined the path parameters twice. Since you have declared it there directly below -http: you can remove the request: parameters: paths: block.
In other words, try this:
updateApplication:
handler: handler.updateApplication
memorySize: 3008
description: Update application
timeout: 30
events:
- http:
path: services/{serviceId}/applications/{applicationId}
method: post
cors: true
authorizer: authorize
Happy coding! 👍

Exclude Lambda function from deploy to a particular stage

I am trying to exclude a Lambda function from being deployed via serverless to my prod stage within AWS.
A snippet from my serverless yaml looks something like -
functions:
some-prod-function:
handler: prodFunction.handler
events:
- http:
path: /prod-function
method: post
some-dev-function:
handler: devFunction.handler
events:
- http:
path: /dev-function
method: post
Is there a way to exclude some-dev-function from being deployed to prod?
You can put those definitions on a different property and use variables in order to choose which definitions to use.
environment-functions:
prod:
some-prod-function:
handler: prodFunction.handler
events:
- http:
path: /prod-function
method: post
dev:
some-dev-function:
handler: devFunction.handler
events:
- http:
path: /dev-function
method: post
functions: ${self:environment-functions.${opt:stage}}
You may need to change this depending on how you specify your stage on deployment (${opt:stage} or ${env:stage}).
I'm using SLS 1.32.0
I wasn't able to get functions: ${self:environment-functions.${opt:stage}} to work. (Not sure why)
It returns the following:
A valid service attribute to satisfy the declaration 'self:environment-functions.dev' could not be found.
However, using the same logic in dashmug's answer, file worked for me:
serverless.yml:
functions: ${file(serverless-${opt:stage}.yml)}
serverless-dev.yml:
some-dev-function:
handler: devFunction.handler
events:
- http:
path: /dev-function
method: post
serverless-prod.yml:
some-prod-function:
handler: prodFunction.handler
events:
- http:
path: /prod-function
method: post
If you are using Serverless framework you could use serverless plugin
serverless-plugin-ifelse
Then
plugins:
- serverless-plugin-ifelse
If you want to exclude say func1
functions:
func1:
name: Function 1
handler: lambda.func1
events:
- http:
path: "path1"
method: "post"
authorizer:
arn: arn:aws:cognito-idp:us-east-1:123456789012:userpool/us-east-1_0acCDefgh
func2:
name: Function 2
handler: lambda.func2
events:
- http:
path: "path2"
method: "post"
func3:
name: Function 3
handler: lambda.func2
events:
- http:
path: "path3"
method: "post"
for us-east-1 . Then use below code snippet
- If: '"${self:provider.region}" == "us-east-1"'
Exclude:
- functions.func1
Create a file for example
env-functions.yml
and add content as below
prod:
some-prod-function:
handler: prodFunction.handler
events:
- http:
path: /prod-function
method: post
dev:
some-dev-function:
handler: devFunction.handler
events:
- http:
path: /dev-function
method: post
After this in serverless.yml set
functions: ${file(env-functions.yml):${opt:stage, self:provider.stage}}

Resources