CFnのテンプレートを1つにしておくと可読性が悪くなったりなどはよく言われているのでNested Stackを使って分割するサンプルを書いてみた。
これをaws cliで扱うようにするとネストされたスタックのファイルが扱いやすくなる。
ネストされたスタック
CloudFormationの最上位のテンプレート内で、別のCloudFormationテンプレートをリソースとして定義することができる、というもの。
これによってテンプレートを分割できる。
試す
VPCの作成とEC2の作成を別テンプレートにして試す。
ディレクトリ構成は以下のようにしてみる。
直下の./master.yml
の中に./ec2/master.yml
と./vpc/master.yml
を呼び出すように定義する。
./ ┗ master.yml ec2/ ┗ master.yml vpc/ ┗ master.yml
各master.yml
の中身は以下のような感じにしている。
./master.yml
AWSTemplateFormatVersion: '2010-09-09' Resources: vpcStack: Type: AWS::CloudFormation::Stack Properties: TemplateURL: ./vpc/master.yml EC2Stack: Type: AWS::CloudFormation::Stack Properties: TemplateURL: ./ec2.yml
TemplateURL
のところでネストするテンプレートを定義する。
通常この部分はアップロードする先のS3のURLを指定しなければならない。
./vpc/master.yml
AWSTemplateFormatVersion: 2010-09-09 Parameters: ProjectNameParameter: Type: String Default: 'sample-cfn' Description: Project name for tag Resources: # VPC vpc1: Type: 'AWS::EC2::VPC' Properties: CidrBlock: 10.1.0.0/16 InstanceTenancy: default EnableDnsSupport: 'true' EnableDnsHostnames: 'false' Tags: - Key: Name Value: !Sub "${ProjectNameParameter}-vpc" # Inernet Gateway igw1: Type: 'AWS::EC2::InternetGateway' Properties: Tags: - Key: Name Value: !Sub "${ProjectNameParameter}-igw" # DHCP Options dopt1: Type: 'AWS::EC2::DHCPOptions' Properties: Tags: - Key: Name Value: !Sub "${ProjectNameParameter}-dopt" DomainName: ap-northeast-1.compute.internal DomainNameServers: - AmazonProvidedDNS # RouteTable rt1: Type: 'AWS::EC2::RouteTable' Properties: VpcId: !Ref vpc1 Tags: - Key: Name Value: !Sub "${ProjectNameParameter}-rt" # NetworkACL nacl1: Type: 'AWS::EC2::NetworkAcl' Properties: VpcId: !Ref vpc1 # NetworkACL Entry acl1: Type: 'AWS::EC2::NetworkAclEntry' Properties: CidrBlock: 0.0.0.0/0 Egress: 'true' Protocol: '-1' RuleAction: allow RuleNumber: '100' NetworkAclId: !Ref nacl1 acl2: Type: 'AWS::EC2::NetworkAclEntry' Properties: CidrBlock: 0.0.0.0/0 Protocol: '-1' RuleAction: allow RuleNumber: '100' NetworkAclId: !Ref nacl1 # InternetGateway gw1: Type: 'AWS::EC2::VPCGatewayAttachment' Properties: VpcId: !Ref vpc1 InternetGatewayId: !Ref igw1 # RouteTable route1: Type: 'AWS::EC2::Route' Properties: DestinationCidrBlock: 0.0.0.0/0 RouteTableId: !Ref rt1 GatewayId: !Ref igw1 DependsOn: gw1 # DHCP Options Association dchpassoc1: Type: 'AWS::EC2::VPCDHCPOptionsAssociation' Properties: VpcId: !Ref vpc1 DhcpOptionsId: !Ref dopt1 # Subnet subnet1: Type: 'AWS::EC2::Subnet' Properties: CidrBlock: 10.1.1.0/24 AvailabilityZone: ap-northeast-1a VpcId: !Ref vpc1 Tags: - Key: Name Value: !Sub "${ProjectNameParameter}-web-public-1a-subnet" mySubnetRouteTableAssociation: Type: 'AWS::EC2::SubnetRouteTableAssociation' Properties: SubnetId: !Ref subnet1 RouteTableId: !Ref rt1 subnetacl1: Type: 'AWS::EC2::SubnetNetworkAclAssociation' Properties: SubnetId: !Ref subnet1 NetworkAclId: !Ref nacl1 ### Outputs Outputs: VpcId: Value: !Ref vpc1 Export: Name: !Sub "${ProjectNameParameter}-vpc" RouteTableId: Value: !Ref rt1 Export: Name: !Sub "${ProjectNameParameter}-rt" NetworkAclId: Value: !Ref nacl1 Export: Name: !Sub "${ProjectNameParameter}-nacl" SubnetId: Value: !Ref subnet1 Export: Name: !Sub "${ProjectNameParameter}-subnet"
./ec2/master.yml
AWSTemplateFormatVersion: 2010-09-09 Parameters: ProjectNameParameter: Type: String Default: 'sample-cfn' Description: Project name for tag Resources: EC2: Type: 'AWS::EC2::Instance' Properties: DisableApiTermination: 'false' InstanceInitiatedShutdownBehavior: stop ImageId: ami-00f9d04b3b3092052 InstanceType: t2.micro KeyName: ec2-key-pair-apn1 Monitoring: 'false' Tags: - Key: Name Value: !Sub "${ProjectNameParameter}-ec2" NetworkInterfaces: - DeleteOnTermination: 'true' Description: Primary network interface DeviceIndex: 0 SubnetId: "Fn::ImportValue": !Sub "${ProjectNameParameter}-subnet" PrivateIpAddresses: - PrivateIpAddress: 10.1.1.45 Primary: 'true' GroupSet: - !Ref sg1 AssociatePublicIpAddress: 'true' sg1: Type: 'AWS::EC2::SecurityGroup' Properties: GroupDescription: 'ds-common-ec2-bastion01-sg created 2018-11-15T15:18:31.279+09:00' VpcId: "Fn::ImportValue": !Sub "${ProjectNameParameter}-vpc" ingress1: Type: 'AWS::EC2::SecurityGroupIngress' Properties: GroupId: !Ref sg1 IpProtocol: tcp FromPort: '22' ToPort: '22' CidrIp: 0.0.0.0/0 egress1: Type: 'AWS::EC2::SecurityGroupEgress' Properties: GroupId: !Ref sg1 IpProtocol: '-1' CidrIp: 0.0.0.0/0 Description: Create EC2 Template
まとめ
ネストされたスタックをaws cliで扱うやり方について書いた。親テンプレートに定義するTemplateURL
は本来、子テンプレートが置いてあるs3のバケットURLを指定する必要があるが、ローカルのディレクトリを指定してaws cliを使ってパッケージングすると、子テンプレートをS3にアップロードした上でTemplateURL
をs3のURLに書き換えてくれるのでその点は便利だと思う。
ただ、テンプレートが肥大化すると結局変数の受け渡しが煩雑になる気はする。