I\'m trying to create a new version of a Lambda function using CloudFormation.
I want to have multiple versions of the same Lambda function so that I can (a) point a
Answer updated for February 2018
You can use AWS SAM (Serverless Application Model), and its sam package
and sam deploy
commands to update Lambda. They are similar to aws cloudformation package
and aws cloudformation deploy
commands, but also let you update Lambda versions automatically.
SAM can package your code (or take ZIP package you created otherwise), upload it to S3, and update the $LATEST
Version of the Lambda from it. (If this is all you need, this can also be done with aws cloudformation
, without SAM; code examples are same as below, but only use CloudFormation
's standard declarations). Then, with SAM, if configured accordingly, you can also automatically publish a Version and update an Alias to point to it. It can also, optionally, use AWS CodeDeploy to gradually move traffic from previous Version to new one, and rollback in case of errors. All this is explained in Safe Lambda deployments.
Technically, the idea is that every time you update the stack, you need your AWS::Lambda::Function
's Code
to point to the new package in S3. This will ensure that when you update the stack, Lambda's $LATEST version will be updated from the new package. Then, you can also automate the publishing of new Version and switch an Alias to it.
For it, create a SAM template, which is similar to (a superset of) CloudFormation template. It may include SAM-specific declarations, like the one for AWS::Serverless::Function
below. Point the Code
to source code directory (or a prepackaged ZIP), and set the AutoPublishAlias
property.
...
MyFunction:
Type: AWS::Serverless::Function
Properties:
... # all usual CloudFormation properties are accepted
AutoPublishAlias: dev # will publish a Version and create/update Alias `dev` to point to it
Code: ./my/lambda/src
...
Run:
$ sam package --template-file template.yaml --output-template-file packaged.yaml --s3-bucket my-bucket
This packages source directory contents as a ZIP (if Code
is not a ZIP already), uploads it to S3 under new autogenerated key, and generates final CloudFormation template to packaged.yaml
, putting for you the proper Code
reference into it; like this:
...
MyFunction:
Properties:
Code:
S3Bucket: my-bucket
S3Key: ddeeaacc44ddee33ddaaee223344
...
Now you can use generated packaged.yaml
with SAM, to create function Version:
sam deploy --template-file packaged.yaml --stack-name my-stack [--capabilities ...]
This will update Lambda's $LATEST
version, and, if AutoPublishAlias
was defined, publish it as a new Version and update the Alias to point to the newly published Version.
See the examples in SAM GitHub repo for a complete template code.
MyLambda:
Type: AWS::Lambda::Function
Properties:
Role: LambdaRole
Code:
S3Bucket: LambdaPackageS3Bucket
S3Key: !Sub "${LambdaPakcageNameWithVersion}"
FunctionName: LambdaFunctionName
Handler: lambda_function.lambda_handler
Runtime: python3.6
Timeout: 60
Unfortunately, this is not possible to do using CloudFormation. You will need to add new AWS::Lambda::Version
sections in your CloudFormation template for each version.
The closest solution would be to create .erb templates and have it generate CloudFormation templates with all the versions.
This is a bit of a hack, and depends on using gitlab-ci (or something similar), but I find passing the commit hash into a cloudformation template (via the template's parameters) very useful.
(It's a bit like @Jerry 's answer, but using the commit hash.)
In this case you could do something like:
Have a parameter in your template for the commit hash, e.g.:
AWSTemplateFormatVersion: '2010-09-09'
Description: Template for Lambda Sample.
Parameters:
ciCommitSha:
Type: String
s3Bucket:
Type: String
...
You can then reference this in the lambda resource, like this:
CFNLambda:
Type: AWS::Lambda::Function
Properties:
FunctionName: cfn_trigger_fn
Description: lambda which gets triggered by cloudformation
Runtime: python3.7
Code:
S3Bucket: !Ref s3Bucket
S3Key: !Join [ ".", [ !Ref ciCommitSha, "zip"]]
Handler: function.handler
...
Your ci pipeline then needs to look something like (assuming you call your cloudformation template stack-template.yaml):
variables:
REGION: us-east-1
S3_BUCKET_NAME: my-bucket
stages:
- build
- push
- deploy
build-package:
stage: build
script:
- some code to produce a deployment package called function.zip
artifacts:
name: deployment_package
paths:
- function.zip
push-code:
stage: push
script:
- aws s3 cp function.zip s3://$S3_BUCKET_NAME/$CI_COMMIT_SHA.zip
deploy-trigger-stack:
stage: deploy
script:
- aws cloudformation deploy
--template-file stack-template.yaml
--stack-name my-stack
--region $REGION
--no-fail-on-empty-changeset
--capabilities CAPABILITY_NAMED_IAM
--parameter-overrides
ciCommitSha=$CI_COMMIT_SHA
s3Bucket=$S3_BUCKET_NAME
You can use this technique for triggering cfn-init on EC2 metadata as well..
This post is out-of-date. I am updating it here so others can see the correct solution for versioning Lambdas as of 06-09-2020, without the need for extra custom versioning Lambdas.
This:
Description: Lambda Example
Resources:
Function:
Type: AWS::Lambda::Function
Properties:
Handler: index.handler
Code:
ZipFile: |
'Example Code';
Runtime: nodejs12.x
Timeout: 5
Becomes this:
Description: Lambda Example
Transform: AWS::Serverless-2016-10-31
Resources:
Function:
Type: AWS::Serverless::Function
Properties:
AutoPublishAlias: live
Handler: index.handler
InlineCode: |
'Example Code';
Runtime: nodejs12.x
Timeout: 5
The Transform:
allows AWS::Serverless::Function
inside of a CloudFormation template which in turn supports lambda versioning.
Don't let the dated "Best Answer" above - built for that persons book - throw you down a rabbit hole like I did.
You're welcome.