Serverless管理下のリソース達が200制限に達したのでNested Stacksに放り込む(by serverless-plugin-split-stacks)

# おーばーびゅー
[こんな感じ](https://qiita.com/wara_be/items/f3ec38e5be6bdea7c79d)でどちゃくそAPI作っていたら、[CloudFormationの制約](https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/cloudformation-limits.html)に当たってしまいdeployできなくなりました...(1 stack内のresource数は最大200まで)
今回はこれを、[Nested Stacks](https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/using-cfn-nested-stacks.html)なるもので解決していきます。
deployには引き続きServerless Frameworkを用いているので、[serverless-plugin-split-stacks](https://github.com/dougmoscrop/serverless-plugin-split-stacks)というプラグインを導入してdeployするresourceを複数のstackに分けていきます。

# 環境

macOS Sierra v10.12.6
node.js v9.7.0
yarn v1.5.1
Serverless Framework v1.26.1
serverless-plugin-split-stacks v1.4.1
# 作業

## install

```
yarn add -D serverless-plugin-split-stacks
```

すっごい不安定そうな指定...そのうち直します

## edit files

### serverless.yml内でpluginsの指定

serverless-webpackが入っていますがこれは必須ではなく、私が使っているだけです。
他のpluginはお使いの環境に合わせて適宜指定してください。

```serverless.yml
...
plugins:
  - serverless-webpack
  - serverless-plugin-split-stacks
...
```

### splitの仕方をカスタマイズ

運用しているリソース数がやっと200に到達したぐらいであれば以下の作業は行わなくともdeploy出来るかと思います。
私のリソース力は~~53万~~400弱まで膨らんでおり、このままではsplitしたNested Stacksでさえ200リソースの制限に引っかかってしまうので、splitのルールを追加で指定していきます。
今回はserverless.ymlのfunctions以下に定義しているfunction名の頭の文字列を見てどのNested Stacksに振り分けるか判断していきます。

下記`stacks-map.js`はprojectのトップレベル(`serverless.yml`と同階層)に配置してください。
`serverless-plugin-split-stacks` が自動で読み込みます。

```stacks-map.js
const ServerlessPluginSplitStacks = require('serverless-plugin-split-stacks');

ServerlessPluginSplitStacks.resolveMigration = function(
  resource,
  logicalId,
  serverless
) {
  // logicalIdに関数名+リソース名が入ってきます
  // ここにはServerlessがdeployするあらゆるリソースが入ってくるので、'LambdaFunction'や'LambdaPermissionApiGateway'などの多様なリソース名で飛んでくるので、そのprefixのみを見て格納先を判断します
  if (logicalId.startsWith('Programs')) {
    // 返却するobjectのdestinationに振り分け先のNested Stacksのprefixを渡します 
    // e.g.: { destination: 'Programs' }を返却するとProgramsNestedStackが作成され、その中にリソースが格納されます
    return { destination: 'Programs' };
  }

  // 上は冗長に書きましたがこんな感じで並べていけば良いです
  if (logicalId.startsWith('Tags')) return { destination: 'Tags' };

  // Fallback to default:
  return this.stacksMap[resource.Type];
};
```

# こうなる

deployの途中でこういう出力が出ます
(若干消している行があるので、このログのリソース数などは合っていません)

```
> sls deploy --stage local
...
Serverless: [serverless-plugin-split-stacks]: Summary: 238 resources migrated in to 9 nested stacks
Serverless: [serverless-plugin-split-stacks]:    Resources per stack:
Serverless: [serverless-plugin-split-stacks]:    - (root): 127
Serverless: [serverless-plugin-split-stacks]:    - APINestedStack: 44
Serverless: [serverless-plugin-split-stacks]:    - ProgramsNestedStack: 12
Serverless: [serverless-plugin-split-stacks]:    - TagsNestedStack: 18
Serverless: [serverless-plugin-split-stacks]:    - VersionsNestedStack: 13
...
```

いぇーーい

# 注意点

## splitのカスタマイズは計画的に

途中で

> 運用しているリソース数がやっと200に到達したぐらいであれば以下の作業は行わなくともdeploy出来るかと思います。

などと書きましたが、特定のresourceはstackを跨いだ移動が不可能です。
[今回使用しているプラグインのREADME.md](https://github.com/dougmoscrop/serverless-plugin-split-stacks/blob/master/README.md)からも引用します。(2018/03/14)

> Many kind of resources (as e.g. DynamoDB tables) cannot be freely moved between CloudFormation stacks (that can only be achieved via full removal and recreation of the stage)

"今はこれで大丈夫だからとりあえずこれでdeploy..."とかせずに、しっかりご自身の計画でsplitの仕方をカスタマイズしていった方が懸命かと思います。

## 多分すぐobsolete

こんなQiita記事を書いておいてなんですが、 `serverless-plugin-split-stacks` はversion 2からLambda function毎にNested Stacksを振り分ける方式になるようです。
その方式をPer Lambdaと呼んでおり、今回使用した方式はPer Typeというものです(実際には関数のprefixで大部分を分けているのでResource Typeによる分割は部分的なものですが)。
目下開発中なようなのでそちらも期待しましょう。

こちらからは以上です。
お疲れ様でした。