Call service from existing api gateway using base path mappings - aws-lambda

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

Related

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

How to use an external layer with the Serverless Framework?

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.

Setting Access Role for Event Stream Created Via CloudFormation

I'm trying to add a dynamodb stream with the following template.yml
MyFunc:
Type: AWS::Serverless::Function
Properties:
CodeUri: ./myfunc
Handler: main
Runtime: go1.x
Events:
MyStream:
Type: DynamoDB
Properties:
Stream: !GetAtt MyTable.StreamArn
BatchSize: 1
StartingPosition: LATEST
Role:
Fn::ImportValue:
!Join ['-', [!Ref 'ProjectId', !Ref 'AWS::Region', 'LambdaTrustRole']]
However, I'm getting the following error during the deploy stage:
Please ensure the role can perform the GetRecords, GetShardIterator, DescribeStream, and ListStreams Actions on your stream in IAM.
Attempt 1
So I tried fixing the problem by adding the following policies to my IAM, CodeStarWorker-myproject-CloudFormation:
"dynamodb:GetRecords",
"dynamodb:GetShardIterator",
"dynamodb:DescribeStream",
"dynamodb:ListStreams",
That didn't work, still giving me the same error
Attempt 2
Tried using policies stead of role in template.yml
MyFunc:
Type: AWS::Serverless::Function
Properties:
CodeUri: ./myfunc
Handler: main
Runtime: go1.x
Events:
MyStream:
Type: DynamoDB
Properties:
Stream: !GetAtt MyTable.StreamArn
BatchSize: 1
StartingPosition: LATEST
Policies:
- IAMFullAccess
- AWSLambdaFullAccess
But it gave me the following error
API: iam:CreateRole User: arn:aws:sts::xxx:assumed-role/CodeStarWorker-xxx-CloudFormation/AWSCloudFormation is not authorized to perform: iam:CreateRole on resource: arn:aws:iam::xxx:role/awscodestar-xxx-lambda-MyFuncRole-1BO7G545IR5IC
Attempt 3
Specifying a role in template.yml
LambdaRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow #allow lambda to assume this role
Principal:
Service:
- lambda.amazonaws.com
Action:
- sts:AssumeRole
Path: "/"
Policies:
- PolicyName: LambdaRolePolicy
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow # allow to write logs to cloudwatch
Action:
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:PutLogEvents
Resource: arn:aws:logs:*:*:*
- Effect: Allow # allow lambda to read from the event stream
Action:
- dynamodb:DescribeStream
- dynamodb:GetRecords
- dynamodb:GetShardIterator
- dynamodb:ListStreams
Resource: "*"
And assign it to MyFunc
Role:
Fn::GetAtt: [ LambdaRole , Arn ]
However, it's also giving me the same error indicating that I'm not authorized to perform iam:CreateRole
Any help?
iam:CreateRole - you would need this action to create a role. The user that you use to run the Cloudformation template would need to include the "CreateRole" action.

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}}

How do I assign function level IamRoleStatements in Serverless Framework?

I want to assign different permissions for different functions listed in my serverless.yml
functions:
hello:
handler: handler.hello
crawl-distributor:
handler: CrawlDistributor.handler
product-scanner:
handler: ProductScanner.handler
iamRoleStatements:
- Effect: Allow
Action:
- dynamodb:*
- lambda:*
Resource: "*"
This doesn't seem to work. When I add the iamRoleStatements at the provider level, it works, but ends up applying the permissions to all the functions.
provider:
name: aws
runtime: nodejs4.3
stage: api
region: us-east-1
profile: dev
iamRoleStatements:
- Effect: Allow
Action:
- dynamodb:*
- lambda:*
Resource: "*"
From docs, you need to create the function role under resources and reference this new role inside your function.
Example:
service: my-test
provider:
name: aws
runtime: nodejs4.3
stage: api
region: us-east-1
profile: dev
functions:
hello:
handler: handler.hello
crawl-distributor:
handler: CrawlDistributor.handler
product-scanner:
role: myDynamoRole
handler: ProductScanner.handler
resources:
Resources:
myDynamoRole:
Type: AWS::IAM::Role
Properties:
RoleName: myDynamoRole
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Action: sts:AssumeRole
Policies:
- PolicyName: myPolicyName
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- dynamodb:*
- lambda:*
Resource: "*"
Using the module serverless-iam-roles-per-function, you can write iamRoleStatements: under the name of each function like the questioner Hexy wrote.
functions:
hello:
handler: handler.hello
crawl-distributor:
handler: CrawlDistributor.handler
product-scanner:
handler: ProductScanner.handler
iamRoleStatements:
- Effect: Allow
Action:
- dynamodb:*
- lambda:*
Resource: "*"
Document of serverless framework
https://www.serverless.com/plugins/serverless-iam-roles-per-function

Resources