CloudFormationの nested stack が使いづらい件

一言で言うと **子スタックのチェンジセットが操作しづらい** ので使いづらい。
いろいろ検索してみたけど日本語の結果が出てこなかったので検索用メモとして。
（こういうときにつけるタグってなんだろう。。。）

## nested stack[^nested-stack] とは

> ネストされたスタックは、他のスタックの一部として作成されたスタックです。

複数のスタックを連携させる方法としては出力値を用いたクロススタック参照[^cfn-output]などもあるが、それよりももっと直接にスタックの親子関係を構築する仕組み。

### 使用例

以下のような複数のテンプレートがあるときに、

- parent.yaml に対して `aws cloudformation package` を実行する
- package処理結果のyamlに対して `aws cloudformation deploy` を実行する

という手順を踏むことで、子テンプレートの内容を含めてまとめて parent.yaml のスタックでリソース管理できる。

```yaml:parent.yaml
AWSTemplateFormatVersion: 2010-09-09
Description: parent template

# 他のスタックをリソースとして定義
Resources:
  ChildStack1:
    Type: "AWS::CloudFormation::Stack"
    Properties:
      TemplateURL: ./child1.yaml

Outputs:
  Child1Policy:
    Description: child 1 policy
    Value: !GetAtt ChildStack1.Outputs.Policy # 子スタックの出力値を参照できる
    Export:
      Name: child1-policy
```

```yaml:child1.yaml
AWSTemplateFormatVersion: 2010-09-09
Description: child 1 stack

# サンプルとしてIAMポリシーを定義
Resources:
  SamplePolicy:
    Type: "AWS::IAM::ManagedPolicy"
    Properties:
      Description: SamplePolicy
      Path: /
      PolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Action: lambda:InvokeFunction
            Resource: "*"

Outputs:
  Policy:
    Description: child 1 policy
    Value: !Ref SamplePolicy
```

```bash:実行手順
# 関連リソースをパッケージング
# -- child1.yaml が関連リソースとしてS3へアップロードされる
# -- アップ先のバケットは先に用意しておくこと
$ aws cloudformation package --template-file parent.yaml \
      --s3-bucket sample-bucket-1234567890 \
      --output-template-file packaged-parent.yaml

# スタックにチェンジセットを作成(まだリソース作成は行わない)
$ aws cloudformation deploy --no-execute-changeset \
      --template-file packaged-parent.yaml \
      --stack-name sample-stack
```

[追記]
この例ではIAMを操作しているため、リソースの作成時には `--capabilities CAPABILITY_IAM`[^capabilities-iam] を指定しないとエラーになる。
具体的には `--no-execute-changeset` をつけない `aws cloudformation deploy` で変更を反映する場合に上記の引数が必要。

[^nested-stack]: [AWS - ネストされたスタックの操作](https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/using-cfn-nested-stacks.html)
[^cfn-output]: [AWS - チュートリアル: 別の AWS CloudFormation スタックのリソース出力を参照する](https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/walkthrough-crossstackref.html)
[^capabilities-iam]: [アプリケーションの機能：IAM ロール、リソースポリシー、ネストされたアプリケーション](https://docs.aws.amazon.com/ja_jp/serverlessrepo/latest/devguide/acknowledging-application-capabilities.html)

これを実行すると sample-stackに以下のチェンジセットが作成される。

![nested-stack-changeset.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/75716/66c34c0a-7c27-35b6-b1a1-68a467322c59.png)

親スタックからは子スタックが新規リソース(更新時は変更リソース)として扱われる。

## 問題点

この時点では ChildStack1 の管理スタックはまだ作成されておらず、このチェンジセットを適用すると child1.yaml の内容はチェンジセットなしに直接適用される。
更新についても同様で、`aws cloudformation deploy --no-execute-changeset` で作成されるチェンジセットで確認できるのは子スタックが変更されるという内容のみで、**子のチェンジセット内で何が変更されるかはわからない** という状況になる。

## 対応方法

[20210923追記]
※@tsuchikazu さんより情報提供いただきました
2020年11月に [AWS CloudFormation 変更セットがネストされたスタックのサポートを開始](https://aws.amazon.com/jp/about-aws/whats-new/2020/11/aws-cloudformation-change-sets-now-support-nested-stacks/?nc1=h_ls) がアナウンスされて、子スタックの変更セットが親スタックから参照できるようになりました。

### 使用例

```bash:事前にchild変更前の内容を適用
# 子スタックをパッケージング
$ aws cloudformation package \
    --template-file parent.yaml \
    --output-template-file packaged-parent.yaml \
    --s3-bucket sample-bucket-1234567890

# スタックを作成、即時反映
$ aws cloudformation deploy \
    --template-file packaged-parent.yaml \
    --stack-name sample-stack \
    --capabilities CAPABILITY_IAM
```

```yaml:child1.yamlの修正、IAMポリシーに権限を追加してみた
AWSTemplateFormatVersion: 2010-09-09
Description: child 1 stack

Resources:
  SamplePolicy:
    Type: "AWS::IAM::ManagedPolicy"
    Properties:
      Description: SamplePolicy
      Path: /
      PolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Action:
              - lambda:InvokeFunction
              - lambda:ListFunctions
            Resource: "*"

Outputs:
  Policy:
    Description: child 1 policy
    Value: !Ref SamplePolicy
```

```bash:チェンジセットを作成する
# 再度子スタックの定義をパッケージング
$ aws cloudformation package \
    --template-file parent.yaml \
    --output-template-file packaged-parent.yaml \
    --s3-bucket sample-bucket-1234567890

# ネストスタックを含むチェンジセットを作成
# Note: この時点で CAPABILITIES_IAM に許可を出さないとチェンジセットがエラーになる
$ aws cloudformation create-change-set \
    --stack-name sample-stack \
    --change-set-name sample-stack-includes \
    --template-body file://packaged-parent.yaml \
    --include-nested-stacks \
    --capabilities CAPABILITY_IAM
```

`create-change-set` に `--include-nested-stacks` をつけることで子スタックもまとめてチェンジセットを作ることができる。
※引数を付けないとデフォルトでは子スタックの変更セットは作成されないので注意

こうするとブラウザからチェンジセットを表示すると以下のようになる。

- 親スタックのチェンジセット
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/75716/bd44ef98-4599-2dc6-fbea-697b61abd034.png)

- 子スタックのチェンジセット(リンク先)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/75716/318e9442-de4e-a2d2-e30a-ed102fecfaf2.png)

### 物足りない点

一応仕組みとしてはネストされたスタックの変更セットが扱えるようにはなったが、もう少しここなんとかしてほしい感があるところについて。

#### `aws cloudformation deploy` で使えない

`aws cloudformation deploy` でこの機能に相当する引数が見当たらないため、現時点では `aws cloudformation create-change-set` でチェンジセットを明示的に作成する手間がある。
できれば `aws cloudformation package` で関連リソースをアップしたら `aws cloudformation deploy` でチェンジセットも実行も簡単に行えるようにしたい。
(むしろ `sam deploy [--no-execute-changeset]` でもっと簡単に(ry ）

ここは調査不足かもしれないので情報あったらコメお願いします。

#### チェンジセットの反映分が一覧して見れない

マネジメントコンソール上の表示でも `aws cloudformation describe-change-set` の出力でも、親チェンジセットと子チェンジセットは別物の扱いなので、一覧して表示する仕組みがない。
スタック一覧みたいに `ネスト表示` のオプションで子チェンジセットの変更内容も親側でまとめて表示できるともっと便利になると思うのだが(´・ω・`)

## 関連情報

英語圏だと関係しそうな情報が出てきた。

- [Change set support for nested stacks? - Discussion Forums](https://forums.aws.amazon.com/thread.jspa?threadID=228489)
    - 3年前に起票されてから対応してほしいという人がちょこちょこ+1している状況
- [Using Change Sets with Nested CloudFormation Stacks](https://theithollow.com/2018/01/29/using-change-sets-nested-cloudformation-stacks/)
    - それっぽいタイトルのサイトになっているが、要は子スタックを直接GUIなりCLIから操作すればチェンジセットも作れるという内容
    - 現状はそのぐらいがんばならないとできないらしい。。

以下はネストされたスタックのチェンジセットについて

- [Change sets for nested stacks](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/change-sets-for-nested-stacks.html)
    - ネストされたスタックのチェンジセットをUIやCLIから操作する方法など
    - 子スタックの変更後にパッケージングしないと動かなかったが、その点の記載がなくて最初どうやって差分を用意するのかについてしばらく悩んだ