AWSCDK for TypeScript で LambdaLayer をデプロイすると前のバージョンが消し飛ぶんだが………

## はじめに

この記事は、[TypeScript Advent Calendar 2019 #13日目](https://qiita.com/advent-calendar/2019/typescript) の記事です。
13日目担当の @is_ryo です！

## 謝辞

あまりTypeScriptの話ではありません…申し訳ございません……

## はじめに(2回目)

私のいるチームではAWSの構成管理をするためにAWSCDKを使っています。
AWSCDKとは、CloudFormation(以下CFn)のテンプレートをTypeScriptやPythonといった言語で生成することができる開発キットです。
ここではAWSCDK自体の説明は省きます。詳しくは[公式ページ](https://aws.amazon.com/jp/cdk/)を参考にして下さい。

## 何が起こったか…

LambdaLayer を試験的に導入していこうとなり、普段通りにCDKをTypeScriptで書いていました。
ソースコードはこんな感じ

```ts:stack.ts
import cdk = require("@aws-cdk/core")

import { LayerVersion, AssetCode, Runtime } from "@aws-cdk/aws-lambda"

export class LambdaLayerStack extends cdk.Stack {
  constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {
    super(scope, id, props)

    new LayerVersion(this, "DemoLambdaLayer", {
      code: AssetCode.fromAsset("lambdaSources/demo_function"),
      compatibleRuntimes: [Runtime.NODEJS_12_X]
    })
  }
}
```

メンバー「よし、ソース書けたからデプロイするで！」

```terminal
$ aws cdk deploy LambdaLayerStack
```

メンバー「コンソールで確認してみるか」

コンソール「デプロイできてるで」

<img width="963" alt="lambda_layer_1.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/95875/81548546-5d89-725d-8ba9-a526f70f7ef7.png">

メンバー「ちょっとLambdaLayerのロジック変えたからデプロイしなおすか」

〜〜省略〜〜

コンソール「デプロイできてるで」

<img width="925" alt="lambda_layer_2.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/95875/d130e634-00e1-8f9b-d815-0ec73aaaf5f5.png">

メンバー「おい…バージョン1どこにやった？」

コンソール「勘のいいガ(ry」

## 原因は何？

CFnが犯人。というかCFnの性質かと思われる。
スタックに定義していないリソースに関してはわざわざDeleteされる場合があります。
例えばAPIGatewayに `/get` と `/post` というresourceがあったときに、そのAPIGatewayに `/get` だけのスタックでUpdateをすると `/post` がDeleteされます。
これと同じように、スタックをUpdateしたときには前のバージョンの情報は持ってないのでDeleteされていました…

## 対処方法

勝手に消されないようにしましょう。
CFｎで生成するリソースに対して `removal policy` というプロパティを設定することができます。
そのプロパティを `Retain` にすることで削除保護されます。
書き方はこんな感じ

```ts:stack.ts
import cdk = require("@aws-cdk/core")

import { LayerVersion, AssetCode, Runtime } from "@aws-cdk/aws-lambda"

export class LambdaLayerStack extends cdk.Stack {
  constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {
    super(scope, id, props)

    const lambdaLayer = new LayerVersion(this, "DemoLambdaLayer", {
      code: AssetCode.fromAsset("lambdaSources/demo_function"),
      compatibleRuntimes: [Runtime.NODEJS_12_X]
    })
    /* ここから追加 */
    const lambdaLayerResource = lambdaLayer.node.findChild(
      "Resource"
    ) as cdk.CfnResource
    lambdaLayerResource.applyRemovalPolicy(cdk.RemovalPolicy.RETAIN)
  }
}
```

### 軽く説明する

`applyRemovalPolicy` という関数を使うためには `cdk.CfnResource` を変数に入れる必要があります。

```ts
const lambdaLayerResource = lambdaLayer.node.findChild(
  "Resource"
) as cdk.CfnResource
```

`applyRemovalPolicy` という関数を使って `removal policy` を設定します。
基本的にデフォルトは `DELETE` になっています。

```ts
lambdaLayerResource.applyRemovalPolicy(cdk.RemovalPolicy.RETAIN)
```

## 試す

メンバー「デプロイや！」

コンソール「デプロイできてるで」

<img width="740" alt="lambda_layer_3.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/95875/361b4067-0525-c0fc-f4c9-9c84ef529482.png">

メンバー「前のバージョンも残ってる！」

## さいごに

いかがでしょうか。
CFnを書いてる方からすると当たり前じゃんって感じなのかもしれませんが、CFnに消されて困るものがあるときは `removal policy` をちゃんと付けてあげましょう。
ただ注意点として一度 `removal policy` を `RETAIN` でデプロイするとCFｎでは削除できなくなります。コンソールから消したりする必要があります。
ではまた！