ネストされたスタックにおける変更セットの挙動について[AWS CodePipeline+AWS CloudFormation]

はじめに

こんにちは。SCSKのふくちーぬです。

前回・前々回の記事では、CI/CD配下でネストされた AWS CloudFormation スタックに対してパイプラインを構築してきました。こちらの記事を読んでいない方は、是非ご一読していただけると幸いです。

今回は、ネストされたスタックのスタック間の依存関係起因による変更セットの挙動を検証したので整理します。

構成図

前回から、サンプルスタック内にEC2が追加されています。

 

CI/CDパイプラインの構成

前回作成したスタックを更新します。スタックを作成していない方は、新規にスタックを作成してください。

Cloud9及びCodeCommitのディレクトリ構成について

ディレクトリ構成は前回から、”ec2.yaml”が追加されました。ファイルの中身は、子スタックとしてEC2を記述したものとなります。

以下のファイルに対して更新があります。

-param.json

-cfn.yaml

-ec2.yaml

param.json

インスタンススタックで使用する、AMIIdを追加しています。

[
  {
      "ParameterKey": "Environment",
      "ParameterValue": "dev" 
  },
  {
      "ParameterKey": "VPCCidrBlock",
      "ParameterValue": "192.168.0.0/16" 
  },
  {
    "ParameterKey": "AMIId",
    "ParameterValue": "ami-012261b9035f8f938" 
  }

] 

cfn.yaml

親スタックでは、インスタンススタックの内容を追記しています。ポイントとして、他の子スタックからサブネットID及びセキュリティグループIDを参照しています。
AWSTemplateFormatVersion: 2010-09-09
Description: Parent Stack
Parameters:
  Environment:
    Type: String
  VPCCidrBlock:
    Type: String
  AMIId:
    Type: String
Resources:
# ------------------------------------------------------------#
#  Network Stack (VPC Subnet RouteTable InternetGateway)
# ------------------------------------------------------------#
  NW:
    Type: AWS::CloudFormation::Stack
    Properties:
      TemplateURL: src/network.yaml
      Parameters:
        Environment: !Ref Environment
        VPCCidrBlock: !Ref VPCCidrBlock
# ------------------------------------------------------------#
#  Security Stack (SecurityGroup)
# ------------------------------------------------------------#
  SECURITY:
    Type: AWS::CloudFormation::Stack
    Properties:
      TemplateURL: src/securitygroup.yaml
      Parameters:
        Environment: !Ref Environment
        VpcId: !GetAtt NW.Outputs.VpcId
# ------------------------------------------------------------#
#  Instance Stack (EC2)
# ------------------------------------------------------------#
  INSTANCE:
    Type: AWS::CloudFormation::Stack
    Properties:
      TemplateURL: src/ec2.yaml
      Parameters:
        Environment: !Ref Environment
        AMIId: !Ref AMIId
        SubnetId: !GetAtt NW.Outputs.SubnetId
        SGId: !GetAtt SECURITY.Outputs.SGId 

ec2.yaml

ここでは、EC2を1台作成する記述がされています。

AWSTemplateFormatVersion: 2010-09-09
Description: EC2 Stack
Parameters:
  Environment:
    Type: String
  AMIId:
    Type: String
  SubnetId:
    Type: AWS::EC2::Subnet::Id
  SGId:
    Type: AWS::EC2::SecurityGroup::Id
# ------------------------------------------------------------#
#  EC2
# ------------------------------------------------------------#
Resources:
  EC2Instance:
    Type: AWS::EC2::Instance
    Properties:
      ImageId: !Ref AMIId
      InstanceType: t2.micro
      SubnetId: !Ref SubnetId
      SecurityGroupIds:
        - !Ref SGId 

CodeCommitへのプッシュ

更新済みの以下のファイルをCodeCommitにプッシュします。

-param.json

-cfn.yaml

-ec2.yaml

EC2の作成

承認ステージにて、変更セットの内容を確認します。インスタンススタックが追加されて、EC2が新規作成されることが分かります。

問題なければ、”承認します”を押下してデプロイしてください。

セキュリティグループの更新

ここから本題です。依存関係による変更セットの挙動を検証するために、セキュリティグループを更新してみます。

今回は、以下のようにインバウンドルールを更新します。

AWSTemplateFormatVersion: 2010-09-09
Description: Security Group Stack

Parameters:
  Environment:
    Type: String
  VpcId:
    Type: AWS::EC2::VPC::Id

Resources:
# ------------------------------------------------------------#
#  SecurityGroup
# ------------------------------------------------------------#          
  SG:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Enable ssh and web access
      VpcId: !Ref VpcId
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 22
          ToPort: 22
          CidrIp: 192.168.0.0/26 #変更
        - IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          CidrIp: 192.168.0.0/26 #変更
      Tags:
        - Key: Name
          Value: !Sub ${Environment}-sg

Outputs:
  SGId:
    Value: !Ref SG

更新済みの”securitygroup.yaml”をCodeCommitにプッシュしてください。

変更セットの確認

数分待つと承認ステージまで進んでいるので、通知されたメールから変更セットを確認してください。

親スタックの変更セットの確認

親スタックの変更セットを確認してみると、”INSTANCE”,”SECURITY”に更新があります。セキュリティグループのみ変更しているはずが、セキュリティスタックに加えてインスタンススタックも更新されるように判断してしまいますね。

子スタックの変更セットの確認(INSTANCE)

子スタックの変更セットの中身を確認します。やはり、EC2の更新(置き換えまで)が発生しそうです。

子スタックの変更セットの確認(SECURITY)

こちらは意図した挙動になっていますね。セキュリティグループが更新されることが分かります。

承認とデプロイ

上記の変更セットの中身を確認したら、”承認します”を押下してデプロイしてください。

デプロイが完了しました。

スタックの挙動の確認(INSTANCE)

子スタックのイベントをみてみます。ここでリソースの作成・更新・削除の順序を確認することができます。

どうやら子スタック(INSTANCE)全体の更新はされているようですが、論理IDであるEC2Instanceの更新はされていないですね

➡EC2に対しての更新はされていないことが明らかになりました。

EC2の置き換え等発生していないことが分かって、安心しましたね!

スタックの挙動の確認(SECURITY)

上記と同様に確認します。論理IDであるSGの更新がされていて、子スタック(SECURITY)全体の更新もされたことが分かります。

➡セキュリティグループに対して更新されたことが明らかになりました。

まとめ

今回の検証で、子スタック間で依存関係がある際に、変更されていない子スタックの変更セットにおいて誤検知が発生していることが判明しました。

以下の注意点を理解した上で、ネストされたスタックを利用する必要があります。下記の理由としては、!GetAtt等で取得する値については変更セットの作成時点では、変更の有無を判断できない仕様によるものとなります。すなわち変更の有無は、変更セットの実行(デプロイ)時に決定されるということですね。

現状CloudFormationでは、 ネストされたスタックにおいて、Outputs等にて依存関係がある場合、参照先の子スタックにて差分が表示される可能性があります。

 

 

最後に

いかがだったでしょうか。

CI/CD配下のネストされたスタックに対して、変更セットの挙動を検証してみました。ネストされたスタックを利用する際に依存関係が複雑な場合、適切な差分を把握することができず、デプロイに少々不安が残る結果になりました。

特にネストされたスタックを利用する場合は、子スタックを分割しすぎないことで子スタック間の依存関係を減らすことが可能なことを念頭においていただければと思います。

本記事が皆様のお役にたてば幸いです。

ではサウナラ~🔥

タイトルとURLをコピーしました