serverless-application-model
serverless-application-model copied to clipboard
Possibility to get AWS::Serverless::RestApi passed a template variable to Template files with Functions only
We have got a problem where the resource limit of CF is hit when transformed from our SAM Template file.
Given all our Lambdas are behind APIs and we want to maintain all APIs from Single API Gateway, and that we now have to split the SAM Template into multiples to make them independently deploy-able, is there a way that we define one template that has only AWS::Serverless::Api
to build out API Gateway with given Swagger file, and then to pass its reference to other template files that will be deploying our AWS::Serverless::Function
s.
We simply want to pass the reference of RestApiId
to the template files as a CFN Parameter.
Is this possible at this moment? As the error on the attempt says otherwise - RestApiId must be a valid reference to an 'AWS::Serverless::Api' resource in same template, which is quite dis-heartening :-(
You can use CloudFormation export and import to pass the parameters from one stack to a different stack: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-stack-exports.html
To deal with resource limit issue, CloudFormation recommends to use nested stacks: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-nested-stacks.html
~~The caveat here is that nested stack does not support SAM. One way to get around that is to only define the non serverless resources in nested stacks and still leave the serverless resources in the top level SAM template.~~ Edit: Nested stacks are now supported, starting November 2018
@honglu thanks for your reply.
We have been doing what you suggested above and now only serverless resources are left in the SAM when we hit the CF limit after transformation.
There were 3 options we came up with and tried but all failed:
- Define API gateway as a separate SAM file, take export of created Gateway. Split Lambdas into multiple Lambda-only SAM files, with all of these taking already created API Gateway as input for their event.properties.RestApiId => That fails due to the limitation that SAM has i.e. the RestApiId must be a valid reference of serverless:api type resource in the same template file.
- Define API gateway as a separate SAM file and create Role/Policies such that it is allowed to invoke required functions. Split Lambdas into multiple Lambda-only SAM files but without any Api type Event defined under it. That way we can create our Lambdas and API Gateway independent of each other and integrate using Swagger and arrange for permissions separately => That fails at SAM Local level, upon which we depend a lot for local run and testing, as that now doesn't know against what event a lambda should listen.
- Define API gateway as a separate SAM file and create Role/Policies such that it is allowed to invoke required functions. Split Lambdas into multiple Lambda-only SAM files but without RestApiId property for event => That gets even worse as now every lambda creates a new Api Gateway for itself assuming that is what it's asked for. So at the end we are still blocked in that catch-22 situation due to variety of limits and limitations of SAM and CF.
So at the end we are still blocked in that catch-22 situation due to variety of limits and limitations of SAM and CF.
Is there any other way or ideas that can help us overcome it?
PS: However I also came across another issue - #335 that indicates the option-1 was working until a few days back, which is really interesting and worth watching progressing on that issue.
Hi Folks, https://github.com/awslabs/serverless-application-model/issues/149 seems to imply one cannot split the api resource from the function resources. This issue seems to imply it is indeed possible, without giving template examples. I would be forever grateful if someone who's managed to do this would post an actual example that works. Many thanks in advance.
We managed to split them before, but no longer. It suddenly stopped working so we had to redo all of our stack structure which meant a downtime in production. See #335
Does anyone know if this issue is dead? Or does anyone know if there is a way to use the aws lambda update-function-code CLI method to update the code and roll it out with checkpoints?
It would also be great if RestApiId
could be of type AWS::ApiGateway::RestApi
instead of just AWS::Serverless::Api
. We're currently using CloudFormation and would like to dry up some code by slowly switching to SAM but this prevents us from doing that.
Nested stacks are now supported in SAM, meaning you can nest resources in other stacks and use the outputs as values in the current stack.
Even though they are supported, when I created the AWS::Serverless::Api
in a separate stack from an AWS::Serverless::Function
I still got the above error (this was two days ago).
Can you post an example that creates this issue? Is it as simple as defining an API in one stack and referencing a Function in that API that is from a nested stack?
Sure! These are two stacks that cause an error.
template.yml
---
AWSTemplateFormatVersion: 2010-09-09
Transform: 'AWS::Serverless-2016-10-31'
Resources:
RestApi:
Type: AWS::Serverless::Api
Properties:
StageName: LATEST
Function:
Type: AWS::Serverless::Function
Properties:
Handler: index.handler
Runtime: nodejs8.10
InlineCode: >-
exports.handler = async function(event) {
return { hello: 'world' };
}
Events:
Request:
Type: Api
Properties:
Path: /hello
Method: GET
RestApiId: !Ref RestApi
Substack:
Type: AWS::CloudFormation::Stack
Properties:
TemplateURL: "./substack.yml"
Parameters:
RestApi: !Ref RestApi
substack.yml
---
AWSTemplateFormatVersion: 2010-09-09
Transform: 'AWS::Serverless-2016-10-31'
Parameters:
RestApi:
Type: String
Resources:
Function:
Type: AWS::Serverless::Function
Properties:
Handler: index.handler
Runtime: nodejs8.10
InlineCode: >-
exports.handler = async function(event) {
return { hello: 'world' };
}
Events:
Request:
Type: Api
Properties:
Path: /world
Method: GET
RestApiId: !Ref RestApi
Thanks for the example. SAM currently requires the API and lambda functions invoked by the API Gateway to be in the same template in order to use the implicit API generation functionality of SAM. This is because the swagger is generated in the template and references the serverless functions using !Ref
.
Are you saying you are hitting the CFN 200 resource limit when your template only contains the API Gateway resources and lambda function (and IAM roles) directly invoked by API Gateway and all other resources are in nested stacks? This would imply that you have maybe ~70 resource+methods all being serviced by separate Lambda functions. Is that correct?
Some alternatives you could try:
- Route multiple resource+methods to the same Lambda function. This requires your Lambda function to be able to route different requests to different handlers in your code. There are a few language-specific frameworks that make this fairly easy. Examples: aws-serverless-express for Node.js, aws-serverless-java-container for Java.
- Use the
Role
property ofAWS::Serverless::Function
to reuse the same role across multiple Lambda functions that handle APIs. There's a security trade-off you may need to make here, but it will save resources.
Adding SAM support for the same intuitive API mapping within the Events
section of an AWS::Serverless::Function
without the API and function being defined in the same template is non-trivial since the AWS::ApiGateway::RestApi
resource expects the full API definition, including function integrations to be defined. If anyone wants to submit an RFC issue proposing how it could be done in SAM, that'd be awesome, but I don't see us prioritizing this without more feedback from customers.
@jlhood: Let med add some more feedback. I am composing serverless applications using the AWS::Serverless::Application resource. In one SAM template I need to nest two applications that both use an API gateway. In this scenario it's not possible to make the two nested applications share the api gateway because:
RestApiId must be a valid reference to an 'AWS::Serverless::Api' resource in same template
Any ideas for workarounds?
@cshenrik Really appreciate the additional context and feedback. Agreed, adding triggers based on resources in your current template to functions defined within a nested application is not made easy by SAM today.
Currently, you would have to manage your own API swagger document to add the nested app function as an integration.
What do you think about the possibility of being able to specify event sources on the AWS::Serverless::Application
resource similar to how it's done on AWS::Serverless::Function
today? Something like this?
NestedApp:
Type: AWS::Serverless::Application
Properties:
Location:
ApplicationId: <app id>
SemanticVersion: 1.0.0
Events:
GetRoot:
Type: Api
Properties:
Path: /
Method: GET
OutputFunctionName: MyHandlerFunctionName
OutputFunctionArn: MyHandlerFunctionArn
So here I have a nested SAR app called NestedApp
that defines two outputs: MyHandlerFunctionName
and MyHandlerFunctionArn
, i.e., what would normally be referenced via !GetAtt NestedApp.Outputs.MyHandlerFunctionName
and !GetAtt NestedApp.Outputs.MyHandlerFunctionArn
. SAM would then automatically add the needed AWS::Lambda::Permission
and update the implicit API swagger document to add an integration to call MyHandlerFunction
defined inside NestedApp
.
Need to flesh out the exact syntax and some more details but wanted to put it out there to get some feedback.
I think adding a new property to Serverless::Api would solve this problem and more. Maybe we call this Paths
for Swagger symmetry.
Just hit the CFN 200 limit today (yes, @jlhood, I am reusing roles) and looking for a workaround led me to this issue.
I initially tried to pass the "AWS::Serverless::Api" as a parameter to "AWS::Serverless::Application" but then I got the dreaded "The REST API doesn't contain any methods" error as it seems CFN is trying to deploy the API before it builds the other nested stacks.
Any workarounds?
EDIT: Looking at my last created CFN stack the problem seems to be the Lambda Permissions is being created 2 times for each lambda function as pointed out in #285. I would love to move to a nested stack solution but #349 prevents me from this.
I really don't want to create a different API endpoint for different resources but that's all I that comes to mind right now.
I think adding a new property to Serverless::Api would solve this problem and more. Maybe we call this
Paths
for Swagger symmetry.
@brettstack I'm thinking of your proposed solution and I'm not sure I follow. How can adding a property to the Serverless:Api make the nested stacks all share the same Serverless:Api reference?
You would use GetAtt on the Lambda Functions Output from the nested stack. The API would then create the permissions and set up the route just like it does when you define API Events on a Function.
@brettstack Yeah it would be interesting to also have a simplified syntax around adding function integrations on the AWS::Serverless::Api
resource itself. I think that would be a nice feature to add. However, that doesn't solve the same problem for if my nested stack contains a function that I want triggered by a non API resource, e.g., an S3 bucket or SNS Topic. The solution I'm proposing would work for all existing event types that you can add to AWS::Serverless::Function
, including API events. Maybe this is beyond the scope of this specific issue, but I've had conversations with other SAR users who have talked about this exact problem for other Lambda event sources so I figure this can solve both problems in one feature.
Although again, I'm not opposed to adding a way to connect functions to AWS::Serverless::Api
resources through properties on AWS::Serverless::Api
instead of only allowing it through AWS::Serverless::Function
.
Are you saying the S3 bucket is defined in the parent template? Can't you just pass through the S3 bucket ARN and SAM will create the permission for you? I suppose SAM also modifies the notification configuration on the bucket (not sure off the top of my head if something similar happens for SNS), so you would lose that (though you can set it yourself).
@brettstack and @jlhood this sounds great. Are you going to put out an RFC, or get this prioritized. I think more people with hit the 200 CFN limit sooner than later due to #285. If #285 is fixed it may give you some breathing room to flesh out the RFC with more customer feedback. My current workaround is to have multiple API endpoints. It’s ugly but it works for now.
BTW keep up the great work. Thanks a million for doing what you guys do.
@brettstack I got excited for a minute because I thought you were saying there is an existing workaround for shared Gateway - but I see you were thinking out loud. +1 for needing this, we're currently breaking up our API set into several separate gateways because of the limit. It's that permission per endpoint that's killing us - we have maybe a dozen lambdas that each handle maybe another dozen endpoints, and with 2 permissions per endpoint, we're blowing right through the max. My first thought was to break up into sub stacks, but the circular dependency/chicken&egg with the gateway and the functions had me running in circles. I'll be looking forward to enhancements in this area!
@jlhood Has there been any traction on this? Not only is this great for applications with a large number of endpoints, but also for the sake of of modularity.
In addition, I'm wondering if another, slightly similar, solution might be the possibility of being able to modularize our Swagger/OpenAPI documents for each function? I apologize if this is slightly out of scope, but I think this ticket illuminates the issue with modularity and AWS SAM. I also apologize if I overlook a reality that would prevent this solution from being implemented in CF. I'm relatively new to architecting serverless backends.
Say I have a few lambda functions each acting as a separate endpoint (GET foo/bar/, POST foo/bar, etc...). For each I could write the specific paths and definitions for that endpoint. On deployment CF would add each APIG endpoint (resources, methods, models, etc..) based off the modular definition.
In the subtemplate of the function, you could have a resource like AWS::Serverless::ApiEndpoint
with a DefinitionBody
or DefinitionUri` parameter the same way you can reference a complete document in an Api resource.
In the master template you would have the AWS::Serverless::Api
resource and the property Paths
property as @brettstack suggested . The paths could reference these endpoint resources and combine them into a single API Gateway instance.
A few caveats with this approach might be sacrificing some of the ability to combine definitions provided by OpenAPI. For example my GET /foo/bar
might return an object very similar to the payload for POST /foo/bar
plus a couple of extra parameters. In OpenAPI I could define the model for the GET response model to be allOf
the POST model plus those extra params. If we took the approach for modularization, then these definitions might live in separate templates. However, I think this trade off would be overshadowed by the ability to actually modularize our OpenAPI documents; a feature that is already built in to the OpenAPI spec.
I realize that this is a non-trivial solution 😄. Despite this issue, I really enjoy using SAM to deploy my backend. Thanks for all the awesome work!
@juniorclarke @sgates @TeddyHandleman Really appreciate the detailed comments and feedback! I think the best path forward is to split this out into a separate RFC issue so we can focus on defining the right syntax/experience for this. I'll go ahead and create that RFC issue.
@jlhood that's great to hear, and thanks. Looking forward to progress on this!
@jlhood Awesome! Thank you so much.
@cshenrik @juniorclarke @sgates @TeddyHandleman Opened #866 with a more generalized proposal that would support more than just the nested app use case. Would really appreciate your feedback!
@elver Your comment has been removed, because it violates our Code of Conduct.
I decided to write my own macro function that will allow me to define shared api gateway in parent stack and then use reference to exported api gateway from separated SAM stacks. You can find my project here https://github.com/4-cube/cf-shared-apigwv2
Currently, only ApiGatewayV2 (HttpApi) is supported and there is no support to specify Auth per event (I plan to add support for this).
I hit this issue when trying to use nested stacks to get around the 200 resource limit. I was able to get things working by following this tutorial. https://mechanicalrock.github.io/2020/04/28/sam-split-lambdas.html
@cstst The tutorial seems to work only with AWS::Serverless::HttpApi. It didn't work on AWS::Serverless::RestApi.