How dynamically add arn cognito to lambda? - aws-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

Related

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

Specify resources allowed to call a function in its AWS SAM Function template

TL; DR: How should I edit the template below so that it can be triggered by a user pool trigger?
I try to crate a CloudFormation template for a Lambda function defining both the services the function can call and be called from. It should be run with a Cognito User Pool trigger.
To do that, I've defined a resource in template of type AWS::Serverless::Function briefly as follows. Watch out the Policies section:
Resources:
MyFunctionResource:
Type: AWS::Serverless::Function
Properties:
FunctionName: MyFunctionName
CodeUri: ./
Handler: "lambda_function.lambda_handler"
MemorySize: 128
Runtime: python3.7
Timeout: 3
Policies:
- Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- "cognito-idp:*"
- "logs:*"
...
Resource: "*"
- Version: "2012-10-17"
Statement:
- Effect: Allow
Action: "lambda:InvokeFunction"
Principal:
Service: cognito-idp.amazonaws.com
Resource: !Sub "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:MyFunctionName"
The second policy I have inserted for restricting the resources can call my function fails during the stack creation:
Policy document should not specify a principal. (Service: AmazonIdentityManagement; Status Code: 400; Error Code: MalformedPolicyDocument; Request ID: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)
When I remove that policy with principal, the access to the function through the user pool trigger is denied.
I figured out that the permissions should be created as a separate resource with type AWS::Lambda::Permission which can take the function name or arn it will be attached to.
Thus, the following logic creates the function with permissions (a.k.a. Function Policy) successfully:
Resources:
MyFunctionResource:
Type: AWS::Serverless::Function
Properties:
FunctionName: MyFunctionName
CodeUri: ./
Handler: "lambda_function.lambda_handler"
MemorySize: 128
Runtime: python3.7
Timeout: 3
Policies:
- Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- "cognito-idp:*"
- "logs:*"
...
Resource: "*"
## Remove this section
# - Version: "2012-10-17"
# Statement:
# - Effect: Allow
# Action: "lambda:InvokeFunction"
# Principal:
# Service: cognito-idp.amazonaws.com
# Resource: !Sub "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:MyFunctionName"
## Add this instead
MyFunctionPermissions:
Type: AWS::Lambda::Permission
Properties:
Action: "lambda:InvokeFunction"
FunctionName: !GetAtt MyFunctionResource.Arn
Principal: "cognito-idp.amazonaws.com"
SourceArn: !Sub "arn:aws:cognito-idp:${AWS::Region}:${AWS::AccountId}:userpool/*"

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! 👍

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

Resources