Amplify cli で作成したS3バケットのTriggerフィルター設定、バケット設定をする


## 利用しているバージョン
- 2022-06-04時点
- amplify cli 8.3.1 で amplify add storage で作成したS3バケット

## やりたいこと
- amplify add storage でS3バケットを作成、そのS3バケットの設定・Trigger のフィルタールール設定

### やりたいことの結果
S3バケット
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1482752/80f12d8b-b0f5-b1b8-02c6-ebf8fea95926.png)

S3バケットのバージョニング
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1482752/93a1685e-0b98-9872-ccef-80c597fa00b6.png)

S3バケットのイベント通知（Trigger）
![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1482752/b21f5563-a4be-fc57-5974-318125154b2c.png)


## やろうとした設定とダメだった、対応
- cloudformationで書き換えようと思ったけど、できない？

### amplify cli により生成されるcloudformationファイル
`amplify/backend/storage/{s3バケット名}/build/cloudformation-template.json` は、amplify cli が自動生成するもので、自分自身で変更してもcliにより上書きされる

cloudformation-template.json の抜粋

```cloudformation-template.json
  "Resources": {
    "S3Bucket": {
      "Type": "AWS::S3::Bucket",
      "Properties": {
        "BucketName": {
          "Fn::If": [
            "ShouldNotCreateEnvResources",
            {
              "Ref": "bucketName"
            },
            {
              "Fn::Join": [
                "",
                [
                  {
                    "Ref": "bucketName"
                  },
                  {
                    "Fn::Select": [
                      3,
                      {
                        "Fn::Split": [
                          "-",
                          {
                            "Ref": "AWS::StackName"
                          }
                        ]
                      }
                    ]
                  },
                  "-",
                  {
                    "Ref": "env"
                  }
                ]
              ]
            }
          ]
        },
        "CorsConfiguration": {
          (略)
        },
        "NotificationConfiguration": {
          "LambdaConfigurations": [
            {
              "Event": "s3:ObjectCreated:*",
              "Function": {
                "Ref": "{amplifyで作成したlambdafunction}Arn"
              }
            },
            {
              "Event": "s3:ObjectRemoved:*",
              "Function": {
                "Ref": "{amplifyで作成したlambdafunction}Arn"
              }
            }
          ]
        }
```

### S3バケットのイベント通知（Trigger）としては、通常のcfnで以下をNotificationConfigurationのLambdaConfigurationsに追加したい

```json
              "Filter": {
                "S3Key": {
                  "Rules": [
                    {
                    "Name": "prefix",
                    "Value": "public/dummy"
                    }
                  ]
                }
              },
```

- 追加後の想定
```json
        "NotificationConfiguration": {
          "LambdaConfigurations": [
            {
              "Event": "s3:ObjectCreated:*",
              "Filter": {
                "S3Key": {
                  "Rules": [
                    {
                    "Name": "prefix",
                    "Value": "public/dummy"
                    }
                  ]
                }
              },
              "Function": {
                "Ref": "{amplifyで作成したlambdafunction}Arn"
              }
            },
            {
              "Event": "s3:ObjectRemoved:*",
              "Function": {
                "Ref": "{amplifyで作成したlambdafunction}Arn"
              }
            }
          ]
        }
```

- 自分でカスタマイズしたいcfnの記載方法が不明。Lambda functionだとIAM roleを修正できたりするけど、S3のリソースへの設定がcfnでできない

## cfnでなく、別の方法があることを知った
- https://github.com/aws-amplify/amplify-cli/issues/9318#issuecomment-995209204
- issueのコメントを見ると、cdk override機能があり、それを使うとcdkとして設定を書くことでoverrideできる（オーバーライドというのが癖あり）

```
amplify override storage
```
をすると、ファイル `amplify/backend/storage/{s3バケット名}/override.ts` が作成される。

- `amplify override` は https://fossamagna.github.io/anatomy-of-amplify-override/ にてamplify cli 7.3からとのこと。細かく説明されています。

- `override.ts`を`cdk`として記載することで `amplify/backend/storage/{s3バケット名}/build/cloudformation-template.json` の設定を読み込み上書きできる。ポイントは読み込みされること、あるセクションを上書きするというのではなく

### cdkとして上書きする

- 以下によりamplify push は通り、S3バケットでのイベント通知が設定できた

```override.ts
export function override(resources: AmplifyS3ResourceTemplate) {

  const currentsettings  = resources.s3Bucket.notificationConfiguration as s3.CfnBucket.NotificationConfigurationProperty;
  const previousLambdaConfig  = currentsettings.lambdaConfigurations as s3.CfnBucket.LambdaConfigurationProperty[];
  const farn = currentsettings.lambdaConfigurations[0].function;
  const newConfig = {
    event: "s3:ObjectCreated:*",
    filter: {
      s3Key: {
        rules: [
          {
            name: "prefix",
            value: "public/dummy"
          }
        ]
      }
    },
    function: farn
  } as s3.CfnBucket.LambdaConfigurationProperty;
  // here we appends custom config with amplify generated config

  //previousLambdaConfig.pop(); // popで取り出して s3:ObjectCreated:* イベントを消す
  //previousLambdaConfig.pop(); // popで取り出して s3:ObjectRemoved:* イベントを消す
  previousLambdaConfig.splice(0); //配列全て削除
  previousLambdaConfig.push (newConfig);
  const notificationConfigurationProperty: s3.CfnBucket.NotificationConfigurationProperty = {
    lambdaConfigurations: previousLambdaConfig
  };

  resources.s3Bucket.notificationConfiguration = notificationConfigurationProperty;
}
```

- `override.ts`の説明
    - previousLambdaConfig.splice(0) で配列に入っている自動生成される設定をクリアしている
    - previousLambdaConfig.pop() で 配列を１個取り出し配列を減らす方法だと、何やってるかわかりにくい

- override.ts に記載したことで amplify cli によりファイル `amplify/backend/storage/{s3バケット名}/build/override.js`ができあがり、`amplify/backend/storage/{s3バケット名}/build/cloudformation-template.json` が以下のように変わった
```json
        "NotificationConfiguration": {
          "LambdaConfigurations": [
            {
              "Event": "s3:ObjectCreated:*",
              "Filter": {
                "S3Key": {
                  "Rules": [
                    {
                      "Name": "prefix",
                      "Value": "public/dummy"
                    }
                  ]
                }
              },
              "Function": {
                "Ref": "{amplifyで作成したlambdafunction}Arn"
              }
            }
          ]
        },
```

### override.tsの記載でダメだった例

- 以下ではダメ、S3イベントが消えてしまう
```override.ts
export function override(resources: AmplifyS3ResourceTemplate) {

  const currentsettings  = resources.s3Bucket.notificationConfiguration as s3.CfnBucket.NotificationConfigurationProperty;
  const farn = currentsettings.lambdaConfigurations[0].function;
  const newConfig = {
    event: "s3:ObjectCreated:*",
    filter: {
      s3Key: {
        rules: [
          {
                      "Name": "prefix",
                      "Value": "public/dummy"
          }
        ]
      }
    },
    function: farn
  } as s3.CfnBucket.LambdaConfigurationProperty;
  const notificationConfigurationProperty: s3.CfnBucket.NotificationConfigurationProperty = {
    lambdaConfigurations: newConfig[0]
  };

  resources.s3Bucket.notificationConfiguration = notificationConfigurationProperty;
}
```


- 以下ではダメ、pushすると現在に対して追加されて `event: "s3:ObjectCreated:*"` が２つになるためダメ。

```override.ts
export function override(resources: AmplifyS3ResourceTemplate) {
  
  const currentsettings  = resources.s3Bucket.notificationConfiguration as s3.CfnBucket.NotificationConfigurationProperty;
  const previousLambdaConfig  = currentsettings.lambdaConfigurations as s3.CfnBucket.LambdaConfigurationProperty[];
  const farn = currentsettings.lambdaConfigurations[0].function;
  const newConfig = {
    event: "s3:ObjectCreated:*",
    filter: {
      s3Key: {
        rules: [
          {
                      "Name": "prefix",
                      "Value": "public/dummy"
          }
        ]
      }
    },
    function: farn
  } as s3.CfnBucket.LambdaConfigurationProperty;
  // here we appends custom config with amplify generated config
  previousLambdaConfig.push(newConfig);
  const notificationConfigurationProperty: s3.CfnBucket.NotificationConfigurationProperty = {
    lambdaConfigurations: previousLambdaConfig
  };

  resources.s3Bucket.notificationConfiguration = notificationConfigurationProperty;
}
```

### S3バケットのバージョニングを有効にする

- 上記までのS3バケットのTrigger設定と同じ `override.ts` に追加する
```override.ts
export function override(resources: AmplifyS3ResourceTemplate) {
  resources.s3Bucket.versioningConfiguration = {
    status: "Enabled"
  };

....
}
```