CloudFormationテンプレート間で値を渡す3つの方法


CloudFormationテンプレート間でリソースIDなどを渡す方法について紹介します。
簡単のため、2つのテンプレートで以下を実施する例で示します。

- VPCを作りVpcIdを渡す
- 渡されたVpcId内にSubnetを作る

## 1. AWS::CloudFormation::Stack を使って !GetAtt \*.Outputs.\* で受け取る

親となるテンプレート(base.yml)を用意して、2つのテンプレートを呼び出す形になります。
値を渡すテンプレート(vpc.yaml)ではOutputsに値を出力し、
親テンプレートのParametersで`!GetAtt` _ResourceName_`.Outputs.`_OutputName_を使って渡します。
受け取るテンプレート(subnet.yaml)はParametersにパラメータを用意しておきます。

なお、vpc.yaml, subnet.yamlは事前にS3にアップしておき、base.yamlのみをCloudFormationで作成します。
（2, 3の方法はvpc.yaml, subnet.yamlをCFで作成します。)

```yaml:base.yaml
AWSTemplateFormatVersion: '2010-09-09'
Resources:
  VPC:
    Type: AWS::CloudFormation::Stack
    Properties:
      TemplateURL: https://s3.amazonaws.com/***/vpc.yaml
  Subnet:
    Type: AWS::CloudFormation::Stack
    Properties:
      TemplateURL: https://s3.amazonaws.com/***/subnet.yaml
      Parameters:
        VpcId: !GetAtt VPC.Outputs.VpcId # !GetAtt で受け渡す
```

```yaml:vpc.yaml
AWSTemplateFormatVersion: '2010-09-09'
Resources:
  VPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: 10.250.250.0/24
Outputs:
  VpcId: # 渡す値を出力する
    Value: !Ref VPC
```

```yaml:subnet.yaml
AWSTemplateFormatVersion: '2010-09-09'
Parameters:
  VpcId: # 受け取る値を定義する
    Type: AWS::EC2::VPC::Id
Resources:
  Subnet:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VpcId
      CidrBlock: 10.250.250.0/28
```

参考: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-stack.html

テンプレートを更新する場合は、S3にアップ、親テンプレートの更新、という流れになります。

この方法はここで使われています。
https://github.com/awslabs/ecs-refarch-continuous-deployment/blob/master/ecs-refarch-continuous-deployment.yaml

## 2. ImportValueを使う(cross reference)

値を渡すテンプレート(vpc.yaml)ではOutputsにExport属性付きで値を出力し、
受け取るテンプレート(subnet.yaml)は`!ImportValue`で取得します。

```yaml:vpc.yaml
AWSTemplateFormatVersion: '2010-09-09'
Resources:
  VPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: 10.250.251.0/24
Outputs:
  VpcId:
    Value: !Ref VPC
    Export: # 渡す値をExportで出力する
      Name: VpcId
```

```yaml:subnet.yaml
AWSTemplateFormatVersion: '2010-09-09'
Resources:
  Subnet:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !ImportValue VpcId
      CidrBlock: 10.250.251.0/28
```

参考:
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/walkthrough-crossstackref.html

この方法の注意点として、テンプレートを更新してExportする値が変わるような場合、更新する前にImportValueしたリソースを**必ず削除**する必要があります。

## 3. Parameter Storeを使う

値を渡すテンプレート(vpc.yaml)ではParamter Storeに値を保存し、
受け取るテンプレート(subnet.yaml)はParametersで取得します。
このとき、TypeはAWS::SSM::Parameter::Value、Value(テンプレート上はDefault)はParameter StoreのNameになります。

```yaml:vpc.yaml
AWSTemplateFormatVersion: '2010-09-09'
Resources:
  VPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: 10.250.252.0/24
  SSMVpcId: # 渡す値をParameter Storeに保存する
    Type: AWS::SSM::Parameter
    Properties:
      Name: /cf-values/VpcId
      Type: String
      Value: !Ref 'VPC'
```

```yaml:subnet.yaml
AWSTemplateFormatVersion: '2010-09-09'
Parameters:
  VpcId: # 受け取る値を定義する
    Type: AWS::SSM::Parameter::Value<AWS::EC2::VPC::Id>
    Default: /cf-values/VpcId # Parameter StoreのNameを指定
Resources:
  Subnet:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VpcId
      CidrBlock: 10.250.252.0/28
```

参考:
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/parameters-section-structure.html#aws-ssm-parameter-types

記述は多くなりますが、Parameter Storeをこのように使うこともできます。

## まとめ

個人的な希望は、2の方法でExportする値を更新するとImportValueしたリソースも自動で更新するようになるとありがたいのですが。
是非、3の方法もお試しください。