I'm finding it hard to understand the difference between SAM template and Cloudformation template. I know that SAM template can be used to define Serverless Applications like Lambda, but how does that make it different from Cloudformation template? Is the syntax different? I can still specify the Lambda definitions in cloudformation template. So, my question is why should I care about SAM? Won't knowing about just cloud formation template be sufficient?
From CloudFormation's perspective, SAM is a transform. Meaning: SAM templates are syntactically equivalent, but they allow you to define your serverless app with more brevity. The SAM template eventually gets expanded into full CFN behind the scenes. If you already know CFN, but want to write less YAML code, SAM may be beneficial to you. The idea is to reduce your effort.
SAM templates are a superset of Cloudformation. Any Cloudformation template can be run through SAM unchanged, and it will work. SAM supports all the types available in Cloudformation templates, so you can think of SAM as "CloudFormation++".
However, SAM also gives you additional "transforms" that allow you to define certain concepts succinctly, and SAM will figure out what you mean and fill in the the missing pieces to create a full, expanded, legal Cloudformation template.
Example: For SAM (and Serverless Framework) users, who deal mostly in Lambda functions, one of the more most useful transforms is the Events property on the Lambda function -- SAM will add all the objects needs to access that function through an API path in API Gateway.
Resources:
HelloWorldFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: HelloWorldFunction
Handler: app.lambdaHandler
Runtime: nodejs12.x
Events: # <--- "Events" property is not a real Cloudformation Lambda property
HelloWorld:
Type: Api
Properties:
Path: /hello
Method: get
The SAM template snippet shown above gets transformed/expanded into several API Gateway objects (a RestApi, a deployment, and a stage). The AWS::Serverless::Function type used in this snippet is not a real Cloudformation type -- you won't find it in the docs. SAM expands it into a Cloudformation template containing a AWS::Lambda::Function object and several different AWS::ApiGateway::* objects that Cloudformation understands.
To give you an idea of how much manual coding this saves you, here's what the expanded version of the above SAM template looks like as a full Cloudformation template:
Resources:
HelloWorldFunctionHelloWorldPermissionProd:
Type: AWS::Lambda::Permission
Properties:
Action: lambda:InvokeFunction
Principal: apigateway.amazonaws.com
FunctionName:
Ref: HelloWorldFunction
SourceArn:
Fn::Sub:
- arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/hello
- __Stage__: "*"
__ApiId__:
Ref: ServerlessRestApi
HelloWorldFunctionRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Action:
- sts:AssumeRole
Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
Tags:
- Value: SAM
Key: lambda:createdBy
ServerlessRestApiProdStage:
Type: AWS::ApiGateway::Stage
Properties:
DeploymentId:
Ref: ServerlessRestApiDeployment_NNN
RestApiId:
Ref: ServerlessRestApi
StageName: Prod
ServerlessRestApiDeployment_NNN:
Type: AWS::ApiGateway::Deployment
Properties:
RestApiId:
Ref: ServerlessRestApi
Description: 'RestApi deployment id: ???'
StageName: Stage
ServerlessRestApi:
Type: AWS::ApiGateway::RestApi
Properties:
Body:
info:
version: '1.0'
title:
Ref: AWS::StackName
paths:
"/hello":
get:
x-amazon-apigateway-integration:
httpMethod: POST
type: aws_proxy
uri:
Fn::Sub: arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${HelloWorldFunction.Arn}/invocations
responses: {}
swagger: '2.0'
HelloWorldFunction:
Type: AWS::Lambda::Function
Properties:
Code:
S3Bucket: aws-sam-cli-managed-default-samclisourcebucket-???
S3Key: temp/???
Tags:
- Value: SAM
Key: lambda:createdBy
Handler: app.lambdaHandler
Role:
Fn::GetAtt:
- HelloWorldFunctionRole
- Arn
Timeout: 3
Runtime: nodejs12.x
Previously, if you were authoring pure Cloudformation, you would have had to code all this by hand, over and over, for each API Gateway endpoint that you wanted to create. Now, with a SAM template, you define the API as an "Event" property of the Lambda function, and SAM (or Serverless Framework) takes care of the drudgery.
In the old days, when we had to do all this by hand, it totally sucked. But now, everything is glorious again.
Like #Luis Colon said, SAM is a transform. What that means, is that at the top of a SAM Template there is a Transform statement that lets CloudFormation know to run an intrinsic function, Transform, on this SAM template to turn it into a CloudFormation template. So, all SAM Templates will eventually be converted into CF templates, but for the end-user in most cases it is easier to just use the SAM template. For instance, for a simple application with Lambdas triggered by a new API you're creating, the SAM template will let you accomplish this in fewer lines than CloudFormation.
To extend this, the Serverless Framework behaves similarly. Serverless is designed to work across platforms (AWS, Azure, etc.). It's syntax looks very similar to SAM, and it too converts the template into the target platform's (ie. AWS) fuller version of the template (ie. CloudFormation template).
You can imagine SAM as an extended form of CloudFormation. SAM makes Serverless/Lambda deployments easier.
Even CloudFormation can deploy lambda scripts using inline scripts but it has a limitation of 4096 characters and you cannot pack custom dependencies, python libraries.
So to make Lambda/Serverless deployments easy SAM is used. SAM is a CLI tool. You cannot find SAM in AWS Console.
In case of python deployment, sam will read the requirements.txt file build a package, and will deploy the package when you wish to sam deploy
So at the end of the day you can write as much lengthy Lambda Code, use as many libraries you want and even import your custom libraries i.e. complete flexibility.
Related
I've created AWS Lambda in C# using Visual Studio that returns some JSON from the API endpoint. Now I want to run that lambda locally. All the examples use AWS SAM, but they create a new function using the SAM template.
When I run the command sam local start-lambda I get an error saying that the template isn't found. So what is certain is that I need template.yaml, but I'm not sure is there a way to generate this template for existing Lambda?
Any help is appreciated!
Check out the Template Anatomy resource on the AWS documentation.
You might find this example helpful (it's greatly simplified). I use NodeJS for development, but the differences between programming languages when you're creating a SAM Template are trivial. The example is an outline for a simple Lambda function someFunction being invoked by an API Gateway (HTTP) event.
AWSTemplateFormatVersion: '2010-09-09'
Transform: 'AWS::Serverless-2016-10-31'
Description: My Serverless Application
Parameters:
# Manually define this in AWS IAM for just the services needed.
lambdaExecutionRole:
Description: 'Required. The role used for lambda execution.'
Type: 'String'
Default: 'arn:aws:iam::nnnnnnnnnnnn:role/LambdaExecutionRole'
Globals:
Function:
Runtime: nodejs10.x
# Environment:
# Variables:
# NODE_ENV: test
# DEBUG: myapp:foo
Resources:
performSomeFunction:
Type: 'AWS::Serverless::Function'
Properties:
FunctionName: performSomeFunction
Handler: lambda.someFunction
CodeUri: ./
Description: description of the function being performed
MemorySize: 256
Timeout: 60
Role:
Ref: lambdaExecutionRole
Events:
# API Gateway proxy endpoint.
ProxyApiRoot:
Type: Api
Properties:
Path: '/'
Method: ANY
ProxyApiGreedy:
Type: Api
Properties:
Path: '/{proxy+}'
Method: ANY
As you're getting started with AWS Lambda, one of the big concepts to keep in mind is how your function will be triggered. Functions are triggered by different kinds of events, and there can be many many different types of events. I tend to use API Gateway, Simple Queue Service and CloudWatch Events to trigger mine, but it entirely depends on your use case.
It turned out that you can export Lambda function, and get the generated .yaml template, which was exactly what I needed.
We have an API which needs to be versioned. We are using API Gateway and Lambda on AWS. All code is deployed using CICD, and we use SAM templates on top of CloudFormation.
NB we are using API Gateway stages to handle different environments (e.g. stage "dev1" points to lambda "dev1-MyService".
We have a resource 'v1' defined as follows:
V1Resource:
Type: AWS::ApiGateway::Resource
Properties:
RestApiId: !Ref Api
ParentId: !GetAtt Api.RootResourceId
PathPart: 'v1'
# proxy traffic to a lambda:
ProxyMethod:
Type: AWS::ApiGateway::Method
Properties:
HttpMethod: ANY
...
Integration:
Type: AWS_PROXY
Uri: arn:aws:apigateway:...:function:MyFunction:v1/invocations'
And a lambda defined like this:
ApiFunction:
Type: AWS::Serverless::Function
Properties:
AutoPublishAlias: "v1"
FunctionName: MyFunction
...
What we want to do is keep the v1 lambda in the system, but when we have breaking changes to the API, we can define a /v2/ resource, pointing to the v2 alias of the lambda
My plan was to define a second resource (V2Resource) and second proxy method, referencing the v2 alias of the lambda, which I could create by bumping AutoPublishAlias to v2 in the same change.
Then the v2 alias remains pointing to the old version (forever in theory) and each time we deploy from then on, the latest version of the API is aliased to v2 (until we need to do another breaking change and introduce v3).
Unfortunately, when I do that, it deletes the old alias v1.
So how am I supposed to use Lambda aliases to do API versioning with API Gateway? With CloudFormation and SAM templates?
You could use AWS::Lambda::Version resource, from the documentation:
creates a version from the current code and configuration of a function. Use versions to create a snapshot of your function code and configuration that doesn't change.
LambdaVersion:
Type: AWS::Lambda::Version
Properties:
FunctionName: !Ref ApiFunction
Description: v1
Where the Description property gets fixed because it does not accept updates. Additionally, you can create some aliases for these version like dev, stage, prod. For example, for dev version this would be
DevAlias:
Type: AWS::Lambda::Alias
Properties:
Description: Dev Alias for Lambda function
FunctionName: !Ref ApiFunction
FunctionVersion: !GetAtt LambdaVersion.Version
Name: DEV
I have a lambda function that needs to be triggered via Amazon API Gateway. Is there a way to include an already existing API (created using the AWS console) into AWS SAM template?
SAM doesn't support the !ImportValue in the template yet.
Issue on Github
On the GitHub of the aws/serverless-application-model there's an open PR for that feature
See here
If you want you could help and contribute to that PR so then you could start using !ImportValue in your SAM template.yml
Else, I suggest you go with the old way, you create a CI/CD with a CloudFormation template that can use the !ImportValue and is linked to an S3 bucket where your lambda function code lives.
Examples of Cloudformation Templates
Update
SAM CLI now support the !ImportValue, the issue on Github is closed.
You can use it as follow
# You need to export the resource that you want to use in another template first
# This goes at the end of your template.yml file, after the Resources
# template.yml in the first repo
Outputs:
myExportedResource:
Value: !Ref TheResource
Export:
Name: !Sub "{environment}-nice-export-name"
# template.yml in the second repo (This obviously goes in Resources)
MyLambda:
Type: AWS::Serverless::Function
Properties:
FunctionName: awesome-lambda
CodeUri: ./dist
Handler: this-file.handler
Role: !GetAtt LambdaRole.Arn
VpcConfig:
SecurityGroupIds:
- !GetAtt SecurityGroup.GroupId
SubnetIds:
- Fn::ImportValue: !Sub "${environment}-nice-export-name"
You can use it exactly as a normal cloudformation template
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-importvalue.html
Note that here I used Fn:ImportValue because I needed to use !Sub but if you don't need to reference a parameter in your import value simply use !ImportValue
I'm trying to deploy AWS Lambda function by using SAM.
What I want to do is to update exsiting lambda function by deploying local source code.
In order to do that, I specified the existing lambda function name as 'FunctionName' in template.yaml like below.
However, 'FunctionName' does only support for creating new function, not updating to existing function.
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-function.html#cfn-lambda-function-functionname
Are there any ways to specify Function Name in SAM in order to update lambda function?
[template.yaml]
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Resources:
HelloWorld:
Type: 'AWS::Serverless::Function'
Properties:
FunctionName: 'hello_world'
Description: ''
Handler: index.handler
MemorySize: 256
Role: 'arn:aws:iam::368834739507:role/lambda_basic_execution'
Runtime: nodejs6.10
Timeout: 120
Using SAM (and/or CloudFormation), you cannot update existing resources.
SAM (and CloudFormation) create and manage their own resources. All resources specified in the template are going to be created when the stack is created. They cannot be "taken over".
Instead, you should allow SAM (or CloudFormation) to create the Lambda function for you, then update users to reference the new function. After that, you can update your code using SAM.
We use Cloud Formation for define a bunch of Lambda functions:
AWSTemplateFormatVersion: '2010-09-09'
Transform:
- 'AWS::Serverless-2016-10-31'
Resources:
MyLambda:
Type: 'AWS::Serverless::Function'
Properties:
Handler: com.handler::MyLambda
Runtime: java8
CodeUri: .
Description: Some desc
MemorySize: 512
Timeout: 15
Role: !Ref LambdaRole
FunctionName: MyLambda
Events:
MyLambdaEvt:
Type: Api
Properties:
RestApiId: !Ref MyApiDef
Path: /lambda/my
Method: get
MyApiDef:
Type: AWS::Serverless::Api
Properties:
DefinitionUri: s3://a-bucket/api-gateway.yml
StageName: prod
Outputs:
ApiUrl:
Description: URL of your API endpoint
Value: !Join
- ''
- - https://
- !Ref MyApiDef
- '.execute-api.'
- !Ref 'AWS::Region'
- '.amazonaws.com/prod'
A CodePipeline generate a changeset and execute it.
In this way all the Lambda function are correctly updated but the API Gateway endpoint are not update correctly and we need to import and deploy the YML in s3://a-bucket/api-gateway.yml manually.
Why the API doesn't update (an educated guess)
In order for a change to be added to a a change set, CloudFormation has to detect a change. If the only thing that changes (for MyApiDef) between deployments is the contents of the .yaml file out on S3, CloudFormation isn't going to detect a change that it needs to add to the change set.
If this API definition lived in the CF template, rather than a file on S3, CF would (obviously) detect every change and update the API for you.
Since the definition lives in S3, and the file name hasn't changed, no change is detected, so nothing gets updated.
Possible work arounds
You have to convince CloudFormation that something has changed with your API definition. These two things worked for me:
Updating the MyApiDef key itself each run works. (MyApiDefv2,
MyApiDefv3, etc)
Updating the DefinitionUri works. (i.e. version the
filename in S3).
Neither of these is great, but appending a version to the filename in S3 seems more reasonable than the other option.
There are probably other ways to convince CloudFormation a change has taken place. Notably, I could not get Variables to work for this purpose.