serverless-s3-removerでオブジェクトが存在するS3もremove

Serverless Framewok便利ですね。

S3を作ったからにはモノを置くと思うんですが、この状態でremoveしようとすると、空じゃないバケットをremoveしてはダメらしくエラーが出ます。

```shell
An error occurred: xxxBucket - The bucket you tried to delete is not empty
```

そこで探し当てたのが[serverless-s3-remover](https://github.com/sinofseven/serverless-s3-remover)
sls remove時に先に指定S3バケットを空っぽにしてくれるという優れもの。
ただちょっと扱いに難ありだったのでここに記録を残します。

ずいぶんメンテされてないようですが、十分使えます。

# 最終的なyaml構成

```yaml
service: ez-s3-remove
provider:
  name: aws
  stage: ${opt:stage, 'dev'}
  region: ${opt:region, 'ap-northeast-1'}
  profile: ${opt:profile, 'default'}
  deploymentBucket: 'sls-deploy-bucket'
resources:
  Resources:
    myBucket:
      Type: AWS::S3::Bucket
      Properties:
        AccessControl: Private
        BucketName:
          Fn::Join:
            - ""
            - - '${self:service}-${self:provider.stage}-'
              - Ref: AWS::AccountId
  Outputs:
    myBucket:
      Value: !Ref myBucket
plugins:
  - serverless-s3-remover
custom:
  remover:
    buckets:
      - ${cf:${self:service}-${self:provider.stage}.myBucket, ''}
```

ここに至る過程を書きます。

ただの日記です。

# 過程

## ベースとなるyamlファイル

S3だけ作る簡単なものです。

```yaml
service: ez-s3-remove
provider:
  name: aws
  stage: ${opt:stage, 'dev'}
  region: ${opt:region, 'ap-northeast-1'}
  profile: ${opt:profile, 'default'}
  deploymentBucket: 'sls-deploy-bucket'
resources:
  Resources:
    myBucket:
      Type: AWS::S3::Bucket
      Properties:
        AccessControl: Private
        BucketName:
          Fn::Join:
            - ""
            - - '${self:service}-${self:provider.stage}-'
              - Ref: AWS::AccountId
```

これだけだとremoveするときにファイルがあるとエラーになってしまいます。

## serverless-s3-removerを追加

```bash
npm i -D serverless-s3-remover
```

プラグインを使うようにyamlを変更。
対象バケット名をRefで参照。

```yaml
service: ez-s3-remove
provider:
  name: aws
  stage: ${opt:stage, 'dev'}
  region: ${opt:region, 'ap-northeast-1'}
  profile: ${opt:profile, 'default'}
  deploymentBucket: 'sls-deploy-bucket'
resources:
  Resources:
    myBucket:
      Type: AWS::S3::Bucket
      Properties:
        AccessControl: Private
        BucketName:
          Fn::Join:
            - ""
            - - '${self:service}-${self:provider.stage}-'
              - Ref: AWS::AccountId
plugins:
  - serverless-s3-remover
custom:
  remover:
    buckets:
      - !Ref myBucket
```

## そして最初のremove

```shell
S3 Remover: Faild: [object Object] may not be empty.
~略~
An error occurred: myBucket - The bucket you tried to delete is not empty
```

だめだ…
何かエラーが出ている。
オブジェクトが消せてないためCloudFormationのエラーも変わりません。

## バケット名の参照の仕方を変えてみる
もともとRefでバケット名がとれるんだっけ？と思い、バケット名と同じ書き方で直接突っ込んでみる

```yaml
custom:
  remover:
    buckets:
      - Fn::Join:
          - ""
          - - '${self:service}-${self:provider.stage}-'
            - Ref: AWS::AccountId
```

remove

```shell
S3 Remover: Faild: [object Object] may not be empty.
~略~
An error occurred: myBucket - The bucket you tried to delete is not empty
```
だめ。
どうやらRefとか関数をかますと動かない模様。


## 解決編

いろいろ調べてこのIssueに到達。
https://github.com/sinofseven/serverless-s3-remover/issues/3#issuecomment-621385163

CloudFormationのOutputsを参照すればいいとのこと。

```yaml
  Outputs:
    myBucket:
      Value: !Ref myBucket
plugins:
  - serverless-s3-remover
custom:
  remover:
    buckets:
      - ${cf:${self:service}-${self:provider.stage}.myBucket, ''}
```

そしてremove

```shell
S3 Remover: Success: ez-s3-remove-dev-xxxxxxxx is empty.
~略~
Serverless: Stack removal finished...
```

やった！成功です！

CloudFormationのOutputsって使いどころよくわからず使ってこなかったんですが、初めて活躍しました。
書き方も簡単ですね。


# まとめ

最近はAWS SDKにも興味があります。TypeScript補完がかなり効くらしいので。

CloudFormationのyamlを手で書くのがつらい…