AWS SAMでLambdaのバージョン管理をしたい件

以前、「AWS SAMで作成したLambdaのバージョン管理することができないか」といった事象を経験したことがあり、その時調べた内容などの備忘録になります。


## AWS SAM におけるUpdateの挙動
まず初めに、AWS SAMにてLambdaバージョンを作成するには、[AutoPublishAlias](https://docs.aws.amazon.com/ja_jp/serverless-application-model/latest/developerguide/sam-resource-function.html#sam-function-autopublishalias)を有効にする必要があります。(これによりLambdaのバージョンとエイリアスが作成。)

結論から言うと、旧Versionの保持についてはAWS::Serverless::Function上のプロパティからも保持するオプションのようなものはありません。

理由としては、以下の通りです。
以下のように、SAMで作成されたCfn スタックをアップデートする前に変更セットを作成します。
その時のリソースの変更部分を確認した結果、
SAM で作成したLambdaのUpdateを実施すると以下のように**古いLambdaバージョンを削除して、新しいバージョンを作成し、Lambdaのエイリアスに最新バージョンを登録する**ような動きが確認できます。
```
CloudFormation stack changeset
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Operation                                      LogicalResourceId                              ResourceType                                   Replacement                                  
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ Add                                          DemoMomentoFunctionVersion92c48a93f7           AWS::Lambda::Version                           N/A                                          
* Modify                                       DemoMomentoFunctionAliasdemo                   AWS::Lambda::Alias                             False                                        
* Modify                                       DemoMomentoFunction                            AWS::Lambda::Function                          False                                        
- Delete                                       DemoMomentoFunctionVersion9215c6e11b           AWS::Lambda::Version                           N/A                                          
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

```

実際にやってみた結果は以下の通りです。
スタック更新前と更新後のリソースを確認した結果、やはりバージョンのリソース`AWS::Lambda::Version`自体が削除され、新規で作成したバージョンがCfnスタックに登録されています。
そのため、旧バージョンがこのままでは保持できないということになります。

変更前) 
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/872532/8df2e70c-b8b4-edc4-3387-c93c81725ca4.png)


変更後)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/872532/18aa38e6-e49b-cbbd-3325-9b0e6de0d453.png)


## 対処法
対処としては結構シンプルでCloudformationのテンプレート 上で`DeletionPolicy: Retain`を追加してあげればリソース自体はCloudformationのスタックの管理対象からは外れますが、Version自体は保持されます。

注意として、Lambdaに関する全てのリソースが保持されるため、スタック自体を削除したい場合は、Retainの記述を削除してから更新をかけた方がよろしいかと。

そのため、以下のようなCloudformationテンプレートで、SAMリソースに対して、`DeletionPolicy: Retain`とすれば良いかと思います。

```yaml
AWSTemplateFormatVersion: "2010-09-09"
Transform: AWS::Serverless-2016-10-31
Description: >
  momento_api_demo

  Sample SAM Template for momento_api_demo

Globals:
  Function:
    Timeout: 30

Resources:
  API:
    Type: AWS::Serverless::Api
    DeletionPolicy: Retain
    Properties:
      Name: demo-sam-api-momento
      EndpointConfiguration:
        Type: REGIONAL
      StageName: demo

  DemoMomentoFunction:
    Type: AWS::Serverless::Function
    DeletionPolicy: Retain
    Properties:
      FunctionName: demo-sam-function-momento
      CodeUri: demo_momento/
      Handler: lambda_function.lambda_handler
      Runtime: python3.10
      AutoPublishAlias: demo
      Architectures:
        - x86_64
      Environment:
        Variables:
          TestEnv: "hoge"
      Events:
        DemoMomento:
          Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api
          Properties:
            Path: /demo_momento
            Method: get
            RestApiId: !Ref API

```

上記の内容でSAMを更新してみました結果は以下の通りです。

Lambdaのバージョンですが、画像のようにバージョン管理できていることが確認できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/872532/8ab0fc53-c703-dbfc-de1f-bd95a88cd59d.png)

ただし、AutoPublishAliasでエイリアスを作成している場合、バージョンは自動的に$LATESTのバージョンに更新されます。

AutoPublishAlias上で作成されるエイリアスのため、エイリアスと関連づけるバージョンを明示的に指定したい場合は、SAMを諦めるかもしくは、AutoPublishAliasを使わず外出しでAWS::Lambda::Aliasを定義する必要があると思います。

## 結論

AWS SAM で作成したLambdaのUpdateは、
- 新バージョンの作成
- エイリアスを新バージョンに設定
- 旧バージョンの削除

という動きをするため、旧バージョンは自動的に削除される挙動をします。

DeletionPolicyをRetainにすることで、旧バージョンを保持しつつ関数を更新することができ、何かあった際は迅速に旧バージョンへ手動でロールバックできます。

ただしエイリアスの明示的なバージョン指定はできないため、もし必要ならAutoPublishAliasを使わず別だしでAWS::Lambda::Aliasを定義する必要があると思います。