I am building a fairly big REST API with AWS Lambda. Language is node.js. There are over 200 functions and few more to come. What EACH of these functions do is connect with a RDS database, get data or save data.
I am deploying this with aws sam tool. Below is the template.yaml. Please note I am posting just one method because from the outer look all methods looks the same except the endpoints they are pointing.
WSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
xxx-restapi
Sample SAM Template for xxx-restapi
# More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst
Globals:
Function:
Timeout: 3
VpcConfig:
SecurityGroupIds:
- sg-041f2459dcd921e8e
SubnetIds:
- subnet-038xxx2d
- subnet-c4dxxxcb
- subnet-af5xxxc8
Resources:
GetAllAccountingTypesFunction:
Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction
Properties:
CodeUri: xxx-restapi/
Handler: source/accounting-types/accountingtypes-getall.getallaccountingtypes
Runtime: nodejs14.x
Events:
GetAllAccountingTypesAPIEvent:
Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api
Properties:
Path: /accountingtypes/getall
Method: get
GetAccountingTypeByIDFunction:
Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction
Properties:
CodeUri: xxx-restapi/
Handler: source/accounting-types/accountingtypes-byid.getbyid
Runtime: nodejs14.x
Events:
GetAllAccountingTypesAPIEvent:
Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api
Properties:
Path: /accountingtypes/getbyid
Method: get
LambdaRole:
Type: 'AWS::IAM::Role'
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Action:
- 'sts:AssumeRole'
Path: /
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
Policies:
- PolicyName: root
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- ec2:DescribeNetworkInterfaces
- ec2:CreateNetworkInterface
- ec2:DeleteNetworkInterface
- ec2:DescribeInstances
- ec2:AttachNetworkInterface
Resource: '*'
Outputs:
# ServerlessRestApi is an implicit API created out of Events key under Serverless::Function
# Find out more about other implicit resources you can reference within SAM
# https://github.com/awslabs/serverless-application-model/blob/master/docs/internals/generated_resources.rst#api
HelloWorldApi:
Description: "API Gateway endpoint URL for Prod stage for functions"
Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/"
All of my methods are fine, they work as expected. However when I try to deploy, this get stuck at CREATE_IN_PROGRESS. But, if I reduce the number of functions and try, it works.
I examined the aws cloud trail logs, found something like below.
ErrorCode: Client.RequestLimitExceeded
Resources: [{"resourceType":"AWS::EC2::SecurityGroup","resourceName":"sg-041f245xxxxd921e8e"},{"resourceType":"AWS::EC2::Subnet","resourceName":"subnet-af5xxxc8"}]
and
ErrorCode: Client.DryRunOperation
Resources: [{"resourceType":"AWS::EC2::SecurityGroup","resourceName":"sg-041f2459xxxx1e8e"},{"resourceType":"AWS::EC2::Subnet","resourceName":"subnet-axxxx3c8"}]
There are multiple events like above. How can I fix this?
CloudFormation is probably creating too many functions at once, hence you're hitting a throttling limit. You could probably classify this as a bug in CloudFormation, so I think you should for sure report this to AWS or the CloudFormation team.
That being said, a possible workaround could be to deploy in steps. Adding some Lambda functions with each update. This is going to be a very cumbersome thing to do, but I don't see another way.
You could always make this process easier by using nested stacks (which you can then uncomment one by one). Maybe you could even circumvent the entire throttling limit with it, depending on how CloudFormation deals with that. But I'm not sure about this.
If you're managing so many resources in a single stack, you're also in danger of hitting other CloudFormation limits (especially since SAM abstracts multiple resources behind a single type). So using nested stacks could also prevent you from hitting those limits in the (near) future.
Related
I have an AWS SAM template where I have a Lambda with an API Gateway and I want to be able to deploy that lambda to different environments such as develop, release, and production as any other project so that everything can be tested well before reaching the final end-user.
So far I have tried creating a parameter in my template which is used to change the environment on deploy and the first time lambda got deployed with its name-develop but when I tried to deploy to release it just substituted name-develop with name-release instead of having both lambdas coexisting.
Here is my template:
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
ocr-app
Sample SAM Template for ocr-app
# More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst
Globals:
Function:
Timeout: 15
Parameters:
Env:
Type: String
AllowedValues:
- develop
- release
- product
# Default: develop
Description: Environment in which the application will be deployed. Allowed values [develop, release, product]
Resources:
OCRFunction:
Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction
Properties:
Environment:
Variables:
ENV: !Ref Env
CodeUri: ocr/
Handler: app.lambdaHandler
FunctionName: !Sub OCRFunction_${Env}
Runtime: nodejs14.x
Policies:
- S3ReadPolicy: # Managed Policy
BucketName: "*"
- AWSLambdaExecute # Managed Policy
Architectures:
- x86_64
Events:
OCR:
Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api
Properties:
Path: /ocr
Method: get
Outputs:
# ServerlessRestApi is an implicit API created out of Events key under Serverless::Function
# Find out more about other implicit resources you can reference within SAM
# https://github.com/awslabs/serverless-application-model/blob/master/docs/internals/generated_resources.rst#api
OCRApi:
Description: "API Gateway endpoint URL for Prod stage for Hello World function"
Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/hello/"
OCRFunction:
Description: "Hello World Lambda Function ARN"
Value: !GetAtt OCRFunction.Arn
OCRFunctionIamRole:
Description: "Implicit IAM Role created for Hello World function"
Value: !GetAtt OCRFunctionRole.Arn
How do I deploy to multiple environments?
One way I found out so far is creating another stack - so a stack per environment. I am not sure if this is the only/best way so I am open for other opinions.
I have to make an HTTP request that takes a long time to receive a response. I don't want AWS Lambda to make this request as I will be charged for the time it is waiting for the response. Is there any way to use AWS Lambda to handle the response without being charged while waiting?
Based on my comment, I would recommend hitting the long poll endpoint often as opposed to staying connected for long periods of time. You can use a CloudWatch Rule to trigger the lambda function every 5 minutes (or whatever interval you choose). You can give the Lambda a short timeout, say 5-10 seconds, which should prevent it from running for too long. I'm assuming the long poll endpoint will guarantee delivery at least once.
Here is some CloudFormation YAML to get you started on the setup. Far from complete, but should get you on the right track.
Description: Automatically hit long poll endpoint
Resources:
#################################################
# IAM Role for Lambda
#################################################
ROLELAMBDADEFAULT:
Type: AWS::IAM::Role
Properties:
RoleName: your-lambda-default
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- edgelambda.amazonaws.com
- lambda.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole
- arn:aws:iam::aws:policy/service-role/AWSLambdaRole
Policies: []
#################################################
# Lambda function
#################################################
LFUNC:
Type: AWS::Lambda::Function
Properties:
Code:
S3Bucket: bucket-with-code
S3Key: code.zip
Description: Some function name
FunctionName: my-function-name
Handler: index.handler
MemorySize: 256
Role: !GetAtt ROLELAMBDADEFAULT.Arn
#choose your runtime here
Runtime: nodejs8.10
Timeout: 6
#################################################
# Rule to trigger the lambda
#################################################
RULE1:
Type: AWS::Events::Rule
Properties:
Name: custom-trigger
Description: Trigger my lambda
ScheduleExpression: rate(5 minutes)
State: ENABLED
Targets:
- Arn: !GetAtt LFUNC.Arn
Id: uniqueid1
I have the following AWS SAM file (showing extract) for a lambda function. The problem is that I'm trying to specify multiple policies and this does not work, I get an error
Resources:
Get:
Type: AWS::Serverless::Function
Properties:
FunctionName: fnStores
Handler: handler.get
Runtime: nodejs6.10
Policies:
-AmazonDynamoDBReadOnlyAccess
-AmazonS3ReadOnlyAccess
This is the error I get
"ARN -AmazonDynamoDBReadOnlyAccess -AmazonS3ReadOnlyAccess is not valid.
On a side note, is it possible to create a custom policy that combines the above two and then use that? If so please provide an example.
The YAML list isn't valid. Need a space between - and the Policy names
Try
Resources:
Get:
Type: AWS::Serverless::Function
Properties:
FunctionName: fnStores
Handler: handler.get
Runtime: nodejs6.10
Policies:
- AmazonDynamoDBReadOnlyAccess
- AmazonS3ReadOnlyAccess
I followed this tutorial to setup an AWS Lambda function that is invoked upon an upload to S3 and populates DynamoDB.
I'm trying to achieve the same with AWS SAM for which I need to define a template.yaml file with the configuration information. I keep getting this error when deploying with Cloudformation -
Failed to create the changeset: Waiter ChangeSetCreateComplete failed: Waiter encountered a terminal failure state Status: FAILED. Reason: Circular dependency between resources: [LambdaPerm]
I could not find a lot of information on this so I'm struggling to debug. What's causing this error and how can I resolve this? Here's my template configuration -
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Resources:
Gradebook:
Type: AWS::Serverless::Function
Properties:
FunctionName: LambdaGradebookServerless
Handler: serverless.LambdaGradebook
Runtime: java8
CodeUri: ./target/serverless-0.0.1-SNAPSHOT.jar
Role: arn:aws:iam::xxxxxxxxxxxx:role/lambda-s3-execution-role
LambdaPerm:
Type: AWS::Lambda::Permission
Properties:
Action: lambda:InvokeFunction
FunctionName:
Ref: Gradebook
Principal: s3.amazonaws.com
SourceAccount:
Ref: AWS::xxxxxxxxxxxx
SourceArn:
Fn::Join:
- ':'
- - arn
- aws
- s3
- ''
- ''
- Ref: gradebookBucket
gradebookBucket:
Type: AWS::S3::Bucket
Properties:
Bucket: gradebook-lambda
NotificationConfiguration:
LambdaConfigurations:
- Event: s3:ObjectCreated:*
Function:
Ref: Gradebook
To avoid this circular dependency, create the S3 bucket and the Lambda function independently, then update the stack with an S3 notification configuration.
If anyone comes searching for this, I wrote a blog to address this scenario.
https://aws.amazon.com/blogs/mt/resolving-circular-dependency-in-provisioning-of-amazon-s3-buckets-with-aws-lambda-event-notifications/
The implementation approach is similar to what jarmod describes in the accepted answer. The event notification is setup later using a CloudFormation custom resource.
I got the circular dependency error and it turns out it was a missing parameter that I was referencing from the resource on the CloudFormation template!.
Does anyone know of a definitive guide for converting cloudformation to yaml in Serverless v1.0?
I can make quite a few things work but I'm completely stuck on how to setup Firehose and allow a lambda to write to it.
I think the resources section would look something like this:
resources:
Resources:
FirehoseBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: my-firehose-bucket
FirehoseDeliveryStream:
Type: AWS::KinesisFirehose::DeliveryStream
Properties:
DeliveryStreamName: "FirehoseDeliveryStream"
S3DestinationConfiguration:
BucketARN:
Fn::Join:
- ''
- - 'arn:aws:s3:::'
- Ref: FirehoseBucket
BufferingHints:
IntervalInSeconds: 60
SizeInMBs: 5
CompressionFormat: GZIP
Prefix: ${prefix}
RoleARN: "arn:aws:iam::${account-number}:role/${project}-${env}-IamRoleLambda"
But I have no idea how to convert the Cloudformation for the IAM section, which is described here: http://docs.aws.amazon.com/firehose/latest/dev/controlling-access.html.
Any pointers, examples or guides on CF->Yaml conversion much appreciated!
The IAM statements for the Lambda Execution Role (ie the role that the Lambda function assumes when executing) is not specified in the resources.
So if you need permissions to do something from inside your Lambda function you need to give the assumed role permission.
This is specified in the provider section. So in your case (I just copied something from your link, you will have to change it to what you need) it will be something like this (assuming nodejs runtime):
provider:
name: aws
runtime: nodejs4.3
iamRoleStatements:
- Effect: Allow
Action:
- firehose:DeleteDeliveryStream
- firehose:PutRecord
- firehose:PutRecordBatch
- firehose:UpdateDestination
Resource:
- arn:aws:firehose:region:account-id:deliverystream/delivery-stream-name
- Effect: Allow
Action:
- logs:PutLogEvents
Resource:
- arn:aws:logs:region:account-id:log-group:log-group-name:log-stream:log-stream-name
Bonus: For general resources specified in cloud formation json format use an online converter from json to yaml. Much easier to get the started that way.