[AWS SAM] テンプレートの階層化（スタックのネスト）

# `AWS::Serverless::Application`を使用したテンプレートの階層化

サービスごとにテンプレートファイルを分けて以下のような構成を作る

```yaml
src/
  ├─ api-gateway/
  │   ├─ template.yaml  # API Gateway定義、API GatewayからコールされるLambda等の定義
  │   ├─ swagger.yaml
  │   ├─ aaa/function.py # Lambda関数
  │   └─ bbb/function.py # Lambda関数
  │
  ├─ iot-core/
  │   ├─ template.yaml  # IoT Coreの定義、デバイス間通信時にコールされるLambda定義等
  │   └─ ccc/function.py # Lambda関数
  │
  ├─ cognito/
  │   ├─ template.yaml  # Cognitoのユーザプール定義、認証時にコールされるLambda等の定義
  │   └─ ddd/function.py # Lambda関数
  │
  │
  ├─ s3/
  │   ├─ template.yaml  # S3のバケット定義、バケット操作時にコールされるLambda等の定義
  │   └─ eee/function.py # Lambda関数
  │    
  └─ template.yaml  # 各サービスのtemplate.yamlをまとめるためのtemplate.yaml
```

各サービスフォルダ以下の`template.yaml`（`api-gateway/template.yaml`等）は、通常通りCloudFormationまたはSAMの書式に従って記述する

`src/template.yaml`で、各サービスフォルダ以下の`template.yaml`で定義されたリソースをアプリケーション（`AWS::Serverless::Application`）として定義する

`AWS::Serverless::Application`の構文については[公式ドキュメント](https://docs.aws.amazon.com/ja_jp/serverless-application-model/latest/developerguide/sam-resource-application.html)参照

`src/template.yaml`は以下のようになる

```yaml:src/template.yaml
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31

Resources:

  CognitoStack:
    Type: AWS::Serverless::Application
    Properties:
      Location: cognito/template.yaml # template.yamlのパス
      Parameters:
        StackName: !Ref AWS::StackName

  ApiGatewayStack:
    Type: AWS::Serverless::Application
    Properties:
      Location: api-gateway/template.yaml
      Parameters:
        StackName: !Ref AWS::StackName
        S3BucketName: !GetAtt S3Stack.Outputs.S3BucketName

  IoTCoreStack:
    Type: AWS::Serverless::Application
    Properties:
      Location: iot-core/template.yaml
      Parameters:
        StackName: !Ref AWS::StackName

  S3Stack:
    Type: AWS::Serverless::Application
    Properties:
      Location: s3/template.yaml
      Parameters:
        StackName: !Ref AWS::StackName

```

`Location`に各template.yamlのパスを指定、
`Parameters`に各template.yamlに渡すパラメータを指定している

`Parameters`で渡された値は以下のように参照する

```yaml
Parameters:
  StackName:
    Type: String

Resources:
  testFunction:
    Type: AWS::Serverless::Function
    Properties:
      FunctionName: !Sub '${StackName}_testFunction'
      ...
```



## テンプレート間のパラメータ受け渡し

例えば`s3/template.yaml`で生成したバケット名を`api-gateway/template.yaml`で参照したい場合、
`s3/template.yaml`は以下のように`Outputs`を使用してバケット名を出力する

```yaml:s3/template.yaml
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31

Parameters:
  StackName:
    Type: String

Resources:
  InfoS3Bucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: !Sub '${StackName}-test-bucket'
      AccessControl: Private
      PublicAccessBlockConfiguration:
        BlockPublicAcls: True
        BlockPublicPolicy: True
        IgnorePublicAcls: True
        RestrictPublicBuckets: True

Outputs:
  S3BucketName:  # バケット名を出力
    Value: !Ref InfoS3Bucket  # 上記InfoS3Bucketで定義したバケットの名前を出力する
```

出力された値は、`src/template.yaml`で他の`template.yaml`に`Parameters`で渡す

```yaml:src/template.yaml
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31

Resources:
  ...

  ApiGatewayStack:
    Type: AWS::Serverless::Application
    Properties:
      Location: api-gateway/template.yaml
      Parameters:
        StackName: !Ref AWS::StackName
        S3BucketName: !GetAtt S3Stack.Outputs.S3BucketName # s3/template.yamlで出力した値を渡す
  ...

  S3Stack:
    Type: AWS::Serverless::Application
    Properties:
      Location: s3/template.yaml
      Parameters:
        StackName: !Ref AWS::StackName
```

## 生成されるスタック

スタックをネストして定義すると、出力結果は以下のように、
各`template.yaml`ごとに「ネストされた」とついたスタックが生成される
![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/695430/bb7bbfbe-f305-8ade-4959-7e415c31f762.png)

親になっているスタックを削除すると、ネストされている全スタックの全リソースが削除される