My goal here is that additional SAM deploy
calls will result in the :staging alias reflecting the latest version and :live will get updated by outside means, but has to be initialized to the same version created at deploy-time.
I'm using SAM deploy and need aliases on my lambdas. Adding them in the initial template is tricky because you can't create an alias with an explicit version number and AWS CloudFormation forces retention on old lambda versions so you CAN NOT know the version without checking resources. This is the best approach I've found so far, but it's cumbersome and doesn't scale nicely (need the same logic for every stack).
This code also depends on cfn-response.js for custom-resource management.
How can I do this better?
template.yaml
GetLatestVersionOfLambdaFunction:
Type: AWS::Serverless::Function
Properties:
FunctionName: GetLatestVersionOfLambdaFunction
Description: Given an existing Lambda Function Name, return the most recent version number
VersionDescription: !Sub ${deployVersionDescription}
AutoPublishAlias: staging
CodeUri: src/
Handler: getLatestLambdaVersionByName.handler
MemorySize: 128
Runtime: nodejs12.x
Timeout: 10
Tracing: Active
Role: !GetAtt GetLatestVersionOfLambdaRole.Arn
SampleLambdaGetMaxVersionFunction:
Type: Custom::FunctionVersion
Properties:
ServiceToken: !GetAtt GetLatestVersionOfLambdaFunction.Arn
FunctionName: !Ref SampleLambdaFunction
SampleLambdaFunction:
Type: AWS::Serverless::Function
Properties:
FunctionName: SampleLambda
AutoPublishAlias: staging
CodeUri: src/
Handler: SampleLambda.handler
MemorySize: 512
Runtime: nodejs12.x
Role: !GetAtt SampleLambdaRole.Arn
SampleLambdaLiveAlias:
Type: AWS::Lambda::Alias
Properties:
FunctionName: !Ref SampleLambdaFunction
FunctionVersion: !GetAtt SampleLambdaGetMaxVersionFunction.version
Name: live
src/getLatestLambdaVersionByName.js
const response = require('./cfn-response.js'),
AWS = require('aws-sdk'),
lambda = new AWS.Lambda({ apiVersion: '2015-03-31' });
exports.handler = function (event, context) {
console.log({ GetLatestLambdaVersionBynameEvent: event });
if (event.RequestType == 'Delete') {
response.send(event, context, response.SUCCESS); //, { Delete: true, event }
return;
}
try {
let params = {
FunctionName: event.ResourceProperties.FunctionName,
MaxItems: 999
};
// Use list-versions-by-function then determine the greatest version # and return it
lambda.listVersionsByFunction(params, function (err, data) {
console.log({ params, err });
if (err) {
console.error(err, err.stack); // an error occurred
response.send(event, context, response.FAILED);
return; // JSON.stringify({ error: err });
} else {
console.log(JSON.stringify({ Versions: data.Versions }));
let version = data.Versions[data.Versions.length - 1].Version;
if (version == '$LATEST') version = 1;
let res = { version };
response.send(event, context, response.SUCCESS, res);
return; // JSON.stringify(res);
}
});
} catch (err) {
let error = { err, stack: err.stack };
response.send(event, context, response.FAILED);
return; // JSON.stringify({ error });
}
}