CloudFormation : Previous Lambda Alias is getting lost when a new alias is created from new version

1.6k views Asked by At

I'm publishing a new version whenever there is an update to the code, and also creating an alias for the version. But the previous alias is getting lost on the new version

Here is my YAML Template snippet

    Description: Publish a new version of a Lambda function whenever the code is updated.
Parameters:
  MyTestString:
    Description: Change this string when code is updated.
    Type: String
    Default: "Test"
  LambdaFuncName:
    Type: String
    Default: stack_over_flow
  LambdaCustomFuncName:
    Type: String
    Default: custom_stack_over_flow
  LambdaVersionNumber:
    Type: String
    Default: MyAlias-001
Resources:
  MyCustomResource:
    Type: Custom::Resource
    Properties:
      ServiceToken: !GetAtt MyFunction.Arn
      MyTestString: !Ref MyTestString
  MyFunction:
    Type: AWS::Lambda::Function
    Properties:
      Handler: index.handler
      Role: !GetAtt LambdaExecutionRole.Arn
      FunctionName:
        Ref: LambdaFuncName
      Code:
        ZipFile: !Sub |
          var response = require('cfn-response');
          exports.handler = function(event, context) {
            return response.send(event, context, response.SUCCESS, {Result: '${MyTestString}'});
          };
      Runtime: nodejs12.x
  LambdaDeploy:
    Type: Custom::LambdaVersion
    Properties:
      ServiceToken: !GetAtt LambdaDeployFunction.Arn
      FunctionName: !Ref MyFunction
      MyTestString: !Ref MyTestString
  LambdaDeployFunction:
    Type: AWS::Lambda::Function
    Properties:
      Handler: "index.handler"
      Role: !GetAtt LambdaExecutionRole.Arn
      FunctionName:
        Ref: LambdaCustomFuncName
      Code:
        ZipFile: !Sub |
          var AWS = require('aws-sdk');
          var response = require('cfn-response');
          exports.handler = (event, context) => {
            console.log("Request received:\n", JSON.stringify(event));
            if (event.RequestType == 'Delete') {
              return response.send(event, context, response.SUCCESS);
            }
            var lambda = new AWS.Lambda();
            lambda.publishVersion({FunctionName: event.ResourceProperties.FunctionName}).promise().then((data) => {
              return response.send(event, context, response.SUCCESS, {Version: data.Version}, data.FunctionArn);
            }).catch((e) => {
              return response.send(event, context, response.FAILED, e);
            });
          };
      Runtime: nodejs12.x
  LambdaAlias:
    Type: 'AWS::Lambda::Alias'
    Properties:
      FunctionName: !Ref MyFunction
      FunctionVersion: !GetAtt 
        - LambdaDeploy
        - Version
      Name: 
        Ref: LambdaVersionNumber
  LambdaExecutionRole:
    Type: AWS::IAM::Role
    DeletionPolicy: Retain
    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: PublishVersion
        PolicyDocument:
          Version: 2012-10-17
          Statement:
          - Effect: Allow
            Action: ['lambda:PublishVersion']
            Resource: '*'
Outputs:
  LambdaVersion:
    Value: !GetAtt LambdaDeploy.Version
  CustomResourceResult:
    Value: !GetAtt MyCustomResource.Result

LambdaVersionNumber Is a Parameter field defined so that i can get a new version everytime

Updated Image for clarity enter image description here

Thanks in advance for any leads or help

1

There are 1 answers

2
Marcin On

You are executing lambda.publishVersion which just returns last version created in data.Version. So your Version will be always increasing by 1, and subsequently, your LambdaAlias will be also updated to point to new value of Version.

If you don't want it to be this way, maybe don't increase Version for each deployment and keep it always on the old version:

                    return response.send(event, context, 
                                         response.SUCCESS, 
                                         { 
                                          Version: (data.Version == 1 ? data.Version : data.Version - 1)}, 
                                         data.FunctionArn);

But probably it would be simpler to control alias from your LambdaDeployFunction. You are creating new versions there, thus it seems as a perfect place to also properly set your alias.