[アップデート]CloudFormationでネストされたスタックの変更セットがサポートされるようになりました

2020.11.20

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

いつものアップデート職人の執筆ではありませんが、ご容赦ください。

はじめに

仕事終わりにTwitterを眺めていたらCloudFormationのNested Stacksの変更セットがサポートしたというツイートを見つけました。(しかも13時間前だった。。)

AWS CloudFormation change sets now support nested stacks

Nested Stacksのこれまでとこれから

ネットワークやAWSサービス管理しやすい単位でテンプレートを管理し、ルートスタックからType: AWS::CloudFormation::Stackで子スタックとしてテンプレートを呼び出していました。新規スタックの作成は何ら問題ないのですが、作成されたAWSリソースに変更を加える場合です。以前の記事でも書いた通り、子スタックのどのAWSリソースが変更されるか確認できませんでした。回避策として子スタックに対して変更セットだけ実施する方法も以前の記事で書いていたのですが、更新する子スタックが多数ある場合は現実的ではありません。

子スタックに対する変更セットが望む声が届いたのか、Nested Stacksの変更セットがサポートされたことで、子スタックで変更されるリソースを確認することができるようになります。

やってみる

今回試したのは、以下の構成です。

子スタック(ネットワーク関連、セキュリティグループ)とルートスタックのテンプレートです。

test-stack.yml


AWSTemplateFormatVersion: "2010-09-09"

Parameters:
  SystemName:
    Type: String
    MinLength: 1
    MaxLength: 5
    Default: nkhr
  EnvironmentName:
    Description: environment name
    Type: String
    AllowedValues: 
      - dev
      - stg
      - prd
    Default: stg
  DeletionProtection: 
    Type: String
    AllowedValues: 
      - true
      - false
    Default: false

Resources: 
  VPC: 
    Type: AWS::CloudFormation::Stack
    Properties: 
      TemplateURL: "https://cf-templete-xxxxxx.s3-ap-northeast-1.amazonaws.com/stg/vpc.yml"
      Parameters: 
        SystemName: !Sub ${SystemName}
        EnvironmentName: !Sub ${EnvironmentName}
  SG: 
    Type: AWS::CloudFormation::Stack
    DependsOn: VPC
    Properties: 
      TemplateURL: "https://cf-templete-xxxxxx.s3-ap-northeast-1.amazonaws.com/stg/sg.yml"
      Parameters: 
        SystemName: !Sub ${SystemName}
        EnvironmentName: !Sub ${EnvironmentName}

vpc.yml


AWSTemplateFormatVersion: "2010-09-09"

Parameters:
  SystemName:
    Type: String
    MinLength: 1
    MaxLength: 5
    Default: nkhr
  EnvironmentName:
    Description: environment name
    Type: String
    AllowedValues: 
      - dev
      - stg
      - prd
    Default: stg
  DeletionProtection: 
    Type: String
    AllowedValues: 
      - true
      - false
    Default: false

Resources:
  VPC:
    Type: AWS::EC2::VPC    
    Properties:
      CidrBlock: 10.11.0.0/16
      EnableDnsSupport: true
      EnableDnsHostnames: true
      InstanceTenancy: default
      Tags:
        - 
          Key: Name
          Value: !Sub ${SystemName}-${EnvironmentName}-vpc
  InternetGateway:
    Type: AWS::EC2::InternetGateway
    
    Properties:
      Tags:
        - 
          Key: Name
          Value: !Sub ${SystemName}-${EnvironmentName}-igw
  DHCPOptions: 
    Type: AWS::EC2::DHCPOptions    
    Properties: 
        DomainName: ap-northeast-1.compute.internal
        DomainNameServers: 
          - AmazonProvidedDNS
        Tags: 
          - 
            Key: Name
            Value: !Sub ${SystemName}-${EnvironmentName}-dopt
  DHCPOptionsAssociation:
    Type: AWS::EC2::VPCDHCPOptionsAssociation    
    Properties:
      VpcId: !Ref VPC
      DhcpOptionsId: !Ref DHCPOptions
  RouteTable1:
    Type: AWS::EC2::RouteTable    
    Properties:
      VpcId: !Ref VPC
      Tags:
        - 
          Key: Name
          Value: !Sub ${SystemName}-${EnvironmentName}-front-rtb
  Route:
    Type: AWS::EC2::Route    
    Properties:
      RouteTableId: !Ref RouteTable1
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref InternetGateway
  AttachInternetGateway:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties: 
      InternetGatewayId: !Ref InternetGateway
      VpcId: !Ref VPC
  NetworkAcl1:
    Type: AWS::EC2::NetworkAcl
    Properties: 
      VpcId: !Ref VPC
      Tags:
        - 
          Key: Name
          Value: !Sub ${SystemName}-${EnvironmentName}-nacl
  NetworkAclEntry1:
      Type: AWS::EC2::NetworkAclEntry
      Properties:
         NetworkAclId: !Ref NetworkAcl1
         RuleNumber: 100
         Protocol: -1
         RuleAction: allow
         Egress: true
         CidrBlock: 0.0.0.0/0
         Icmp:
            Code: -1
            Type: -1
         PortRange:
            From: -1
            To: -1
  NetworkAclEntry2:
      Type: AWS::EC2::NetworkAclEntry
      Properties:
         NetworkAclId: !Ref NetworkAcl1
         RuleNumber: 100
         Protocol: -1
         RuleAction: allow
         Egress: false
         CidrBlock: 0.0.0.0/0
         Icmp:
            Code: -1
            Type: -1
         PortRange:
            From: -1
            To: -1
  NetworkAclAssociation1:
    Type: AWS::EC2::SubnetNetworkAclAssociation
    Properties:
      SubnetId: !Ref Subnet1
      NetworkAclId: !Ref NetworkAcl1
  Subnet1:
    Type: AWS::EC2::Subnet
    Properties: 
      AvailabilityZone: ap-northeast-1a
      CidrBlock: 10.11.0.0/24
      VpcId: !Ref VPC
      MapPublicIpOnLaunch: false
      Tags:
        - 
          Key: Name
          Value: !Sub ${SystemName}-${EnvironmentName}-front-a-subnet
  Subnet1RouteTableAssciation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref Subnet1
      RouteTableId: !Ref RouteTable1

Outputs:
  VPC:
    Value: !Ref VPC
    Export:
      Name: !Sub ${SystemName}-${EnvironmentName}-vpc
  Subnet1:
    Value: !Ref Subnet1
    Export:
      Name: !Sub ${SystemName}-${EnvironmentName}-subnet1

sg.yml


AWSTemplateFormatVersion: "2010-09-09"

Parameters:
  SystemName:
    Type: String
    MinLength: 1
    MaxLength: 5
    Default: nkhr
  EnvironmentName:
    Description: environment name
    Type: String
    AllowedValues: 
      - dev
      - stg
      - prd
    Default: stg
  DeletionProtection: 
    Type: String
    AllowedValues: 
      - true
      - false
    Default: false

Resources:
  ALBSG:
    Type: AWS::EC2::SecurityGroup
    Properties: 
      VpcId: {"Fn::ImportValue": !Sub "${SystemName}-${EnvironmentName}-vpc"}
      GroupDescription: ALB SG
      SecurityGroupIngress: 
        -
          IpProtocol: tcp
          FromPort: 443
          ToPort: 443
          CidrIp: 0.0.0.0/0
        -
          IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          CidrIp: 0.0.0.0/0
      Tags:
        - 
          Key: Name
          Value: !Sub ${SystemName}-${EnvironmentName}-alb-sg
  EC2SG:
    Type: AWS::EC2::SecurityGroup
    Properties: 
      VpcId: {"Fn::ImportValue": !Sub "${SystemName}-${EnvironmentName}-vpc"}
      GroupDescription: WEB SG
      SecurityGroupIngress: 
        -
          IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          SourceSecurityGroupId: !Ref ALBSG
        -
          IpProtocol: tcp
          FromPort: 22
          ToPort: 22
          CidrIp: 106.xxx.xxx.xxx/32
      Tags:
        - 
          Key: Name
          Value: !Sub ${SystemName}-${EnvironmentName}-web-sg
  EC2SGIngressRule1:
    Type: AWS::EC2::SecurityGroupIngress
    Properties: 
      GroupId: !Ref EC2SG
      IpProtocol: tcp
      FromPort: 22
      ToPort: 22
      CidrIp: 105.xxx.xxx.xxx/32

Outputs:
  ALBSG:
    Value: !Ref ALBSG
    Export:
      Name: !Sub ${SystemName}-${EnvironmentName}-alb-sg
  EC2SG:
    Value: !Ref EC2SG
    Export:
      Name: !Sub ${SystemName}-${EnvironmentName}-ec2-sg

 

ルートスタックのテンプレートを選択してスタックを作成後、子スタックの一部を変更してS3に格納し、ルートスタックから「既存スタックの変更セットの作成」を実行してみます。

ルートスタックに変更はないので「現在のテンプレートの使用」を選択します。

「ステップ 3 スタックオプションの設定の変更セット」にネストされたスタックの変更セット項目が追加されているので有効(デフォルト)にして変更セットを作成します。

変更セットが完了すると変更タブに子スタックの変更セットが表示できるようになっています。それぞれの子スタックの変更セットを見てみると対象の論理IDが確認することができます。

変更セット確認後、親スタックを更新します。変更セットの履歴にも上記と同じ内容が確認できます。

さいごに

今回のアップデートでコードによるインフラの運用管理が更に安心して実現しやすくなったと思います。なお、CloudFormationが利用できるすべてのAWSリージョンで追加料金なしで利用できます。既存のNeted Stacksにも適用されるのでCloudFormationの活用が広まるきっかけになるアップデートだと感じました。