5
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

CloudFormationをゼロから勉強する。(その8:スタックのネスト)

Last updated at Posted at 2020-11-11

はじめに

本記事でも何度か紹介しているAWS Black Belt Online Seminarですが、また新しいセミナーが公開されたようです。

3か月くらい前にもCloudFormationのセミナーが公開されていましたが、それだけCloudFormationの需要があるということなんでしょうね。


その6ではTransformを使用した他テンプレートのインクルードを試しましたが、今回はスタックをネスト構成にしてテンプレートを分割してみようと思います。

ネストとは

イメージとしてはその6Transformでのテンプレート分割と同じような方法となりますが、Transformのインクルードはベーステンプレートの一部を別ファイルにして呼び出すイメージなので、実行単位であるスタックとしては1スタックであるのに対して、ネストは他のスタックを呼び出すイメージとなるので、実行時の見え方は複数スタックとなります。

共通コンポーネント用スタックを作成して、個々のスタックから呼び出すようにするのがAWSのベストプラクティスとのことです。


・・・とはいえTransformのインクルードでも書き方次第でネストした場合と同じことができそうなのでいまいち区別がし辛いですが、ネストの場合はその6で書いたようなyamlの書き方の制約は無いので、Transformのインクルードはリソースの一部パラメータ(例えばタグ)を分割したりするのに使い、ネストはリソース自体を分割したりするのに使うといった区別が良いかと思います。

ネストの書式

ResourcesセクションにAWS::CloudFormation::Stackタイプのリソースを作成することでネストしたスタックを指定できます。

ネストしたファイル名を指定する必要があるため、PropertiesTemplateURLの指定が必須となります。

また、S3バケットへのアップロードが必要となるため、Transformでのインクルードと同様、実行する前にpackageコマンドでURLの変換、S3バケットへのアップロードを行う必要があります。

ネストしたスタックの指定方法(抜粋)
Resources:
  NestStack1:
    Type: AWS::CloudFormation::Stack
    Properties:
      TemplateURL: ./vpc.yaml

ネストしたスタックへの入出力

ネストのスタックは親子関係となるため、親スタックから子スタックを呼び出す構成になります。

入出力の関係は子スタックプログラミング言語の関数として考えるとイメージが湧きやすいと思いますが、親スタック子スタックからの入出力を受ける場合、子スタック(という関数)に引数を渡すのが入力で、子スタック(という関数)からの戻り値が出力となります。

子スタック側での入出力書式

これはネストしていない場合と同じくParametersセクションで上位からの入力を受けて、Outputsで結果を上位へ渡します。

親スタック側での入力書式

親スタックから子スタックへ値を入力するためにはネストするスタックのPropertiesParametersを記載して子スタックに渡す値を記載する必要があります。

上述の例で言うと、親スタックから子スタック(という関数)へ引数を渡す方法が、PropertiesParametersとなります。

子スタックへの入力値指定例
Resources:
  NestStack1:    ★子スタック名
    Type: AWS::CloudFormation::Stack
    Properties:
      TemplateURL: ./vpc.yaml
      Parameters:
        NestVPCRange: !Ref VPCRange
        NestSubnetRange1: !Ref SubnetRange1

親スタック側での出力書式

子スタック(という関数)からの戻り値は!GetAtt組み込み関数で以下の書式で記載することで取得できます。

出力値(戻り値)の取得
!GetAtt [子スタック名].Outputs.[子スタック側のOutputリソース名]

例えば親スタックで前項目の子スタックへの入力値指定例の設定、子スタックで以下のような出力設定を行ったとします。

子スタック側でのOutput(抜粋)
Outputs:
  NestSubnet1Id:    ★Outputリソース名
    Value: !Ref EC2Subnet1

その場合、子スタックの出力値(戻り値)を取得するためには以下の指定を行うことで取得できます。

親スタック側でのNestStack1出力値の取得例
!GetAtt NestStack1.Outputs.NestSubnet1Id

ネストスタック間の入出力まとめ

ネストスタック間の入出力について、AWSマネジメントコンソール画面からの入出力も含めたイメージ図を描いてみました。

子スタックとのやり取り、自スタックでのやり取りとなります。

Stack.png

基本のイメージが分かれば、図のような複雑なスタック構成でも悩むことはないかと思いますが、注意点として、各スタック間の値の入出力は単純なネストの記述ではスタックを飛び越してやり取りできません。

そのため、例えば図中のNestStack2の出力値をNestStack3で使用したい場合は、まずNestStack1OutputNestStack2の戻り値をRoot Stackに渡し、次にRoot StackからNestStack3ParametersNestStack3に値を引き渡す必要があります。

もしスタックを飛び越してやり取りしたい場合は、Outputセクションで値をExportすればやり取りすることが可能なようです。
Exportする方法(Cross Stack Reference)は次回試そうと思います。

今回作成する構成

今回は以下のような構成を作ってみようと思います。

Nest.png

ルートスタック用テンプレートの作成

子スタックで使用するVPCRangeSubnetRange1SubnetRange2を読み込み、各スタックの入力に引き渡します。

また、vpc.yamlで作成するVPC IDsubnet1.yamlで作成するSubnet1のIDsubnet2.yamlec2.yamlで使用するため、vpc.yamlの出力値をsubnet2.yamlec2.yamlの入力値として!GetAttで指定するようにします。

ルートスタック用テンプレート(cf.yaml)
AWSTemplateFormatVersion: 2010-09-09
Resources:
  NestStack1:
    Type: AWS::CloudFormation::Stack
    Properties:
      TemplateURL: ./vpc.yaml
      Parameters:
        NestVPCRange: !Ref VPCRange
        NestSubnetRange1: !Ref SubnetRange1
  NestStack3:
    Type: AWS::CloudFormation::Stack
    Properties:
      TemplateURL: ./subnet2.yaml
      Parameters:
        NestVpcId: !GetAtt NestStack1.Outputs.NestVpcId
        NestSubnetRange2: !Ref SubnetRange2
  NestStack4:
    Type: AWS::CloudFormation::Stack
    Properties:
      TemplateURL: ./ec2.yaml
      Parameters:
        NestSubnetId: !GetAtt NestStack1.Outputs.NestSubnet1Id
Parameters:
  VPCRange:
    Type: String
    Description: "VPC Subnet Range"
  SubnetRange1:
    Type: String
    Description: "Subnet Range1"
  SubnetRange2:
    Type: String
    Description: "Subnet Range2"

VPCスタック用テンプレートの作成

VPC Rangeと、子スタックとなるsubnet1.yamlSubnet1 Rangeの情報をParametersセクションで受け取り、subnet1.yamlSubnet1 Rangeの値を渡します。

また、Subnet1のIDは、ec2.yamlでも使用するため、subnet1.yamlの出力値をvpc.yamlの出力値としてルートスタックとなるcf.yamlに渡すようにします。

VPCスタック用テンプレート(vpc.yaml)
AWSTemplateFormatVersion: 2010-09-09
Parameters:
  NestVPCRange:
    Type: String
  NestSubnetRange1:
    Type: String
Resources:
  EC2VPC1:
    Type: 'AWS::EC2::VPC'
    Properties:
      CidrBlock: !Ref NestVPCRange
      Tags:
        - Key: "Name"
          Value: "cf_VPC1"
  NestStack2:
    Type: AWS::CloudFormation::Stack
    Properties:
      TemplateURL: ./subnet1.yaml
      Parameters:
        NestVpcId: !Ref EC2VPC1
        NestSubnetRange1: !Ref NestSubnetRange1
Outputs:
  NestVpcId:
    Value: !Ref EC2VPC1
  NestSubnet1Id:
    Value: !GetAtt NestStack2.Outputs.NestSubnet1Id

Subnet1スタック用テンプレートの作成

親スタックとなるvpc.yamlからの値をParametersで受け、Subnet1のIDOutputsで出力するようにします。

Subnet1スタック用テンプレート(subnet1.yaml)
AWSTemplateFormatVersion: 2010-09-09
Parameters:
  NestVpcId:
    Type: 'AWS::EC2::VPC::Id'
  NestSubnetRange1:
    Type: String
Resources:
  EC2Subnet1:
    Type: 'AWS::EC2::Subnet'
    Properties:
      VpcId: !Ref NestVpcId
      CidrBlock: !Ref NestSubnetRange1
      Tags:
        - Key: "Name"
          Value: "cf_Subnet1"
Outputs:
  NestSubnet1Id:
    Value: !Ref EC2Subnet1

Subnet2スタック用テンプレートの作成

subnet2.yamlは他スタックで使用する値は無いので、親スタックとなるcf.yamlからの値をParametersで受けるだけになります。

Subnet2スタック用テンプレート(subnet2.yaml)
AWSTemplateFormatVersion: 2010-09-09
Parameters:
  NestVpcId:
    Type: 'AWS::EC2::VPC::Id'
  NestSubnetRange2:
    Type: String
Resources:
  EC2Subnet2:
    Type: 'AWS::EC2::Subnet'
    Properties:
      VpcId: !Ref NestVpcId
      CidrBlock: !Ref NestSubnetRange2
      Tags:
        - Key: "Name"
          Value: "cf_Subnet2"

EC2スタック用テンプレートの作成

subnet1.yamlで出力したSubnet1のIDParametersで読み込み、EC2インスタンスを作成します。

EC2スタック用テンプレート(ec2.yaml)
AWSTemplateFormatVersion: 2010-09-09
Parameters:
  NestSubnetId:
    Type: 'AWS::EC2::Subnet::Id'
Resources:
  EC2Instance:
    Type: 'AWS::EC2::Instance'
    Properties:
      ImageId: ami-0cc75a8978fbbc969
      InstanceType: t2.micro
      KeyName: staging_key
      NetworkInterfaces:
        - AssociatePublicIpAddress: "true"
          SubnetId: !Ref NestSubnetId
          DeviceIndex: 0

テンプレートの実行

ネスト化により、テンプレートが複数に分かれるため、その6と同様、packagedeployコマンドでテンプレートの変換と実行を行います。

URLなどのテンプレートの変換は、ルートスタックで行えば、子スタックも自動的に変換とS3への転送が行われるため、ルートスタック用テンプレートのみ実行します。

テンプレートの変換とS3バケットへの転送
aws cloudformation package --template-file cf.yaml --s3-bucket [S3バケット名] --output-template-file output.yaml

packageコマンド実行後、deployコマンドでS3バケットに転送したテンプレートを実行します。

テンプレートの実行
aws cloudformation deploy --template-file output.yaml --stack-name stack-test --parameter-overrides VPCRange=172.24.0.0/16 SubnetRange1=172.24.0.0/24 SubnetRange2=172.24.1.0/24

CloudFormationスタック画面で以下の様になれば成功です。

capture_18102020_142519.jpg

おわりに

スタックネストは親子関係となるため、構造が分かりやすく、書き方とイメージを覚えてしまえば分かりやすいと感じました。

次も同じようなテンプレート分割方法となるクロススタック参照Cross Stack Reference)を勉強してみようと思います。

5
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?