はじめに
下記のような定番構成をテンプレート化したい
※auto scailingは、テンプレートファイルに記述していません。
テンプレートファイルは複数に分割して、可読性と変更のしやすさを向上させたいので、
cloudformationでスタックをネストした構成にする
cloudformation ネストされたスタック とは
簡単に言うと
親スタックが子のスタックを作成すること
1つのテンプレートファイルに全てのリソースを記述には、行数が膨大になりリソースごとに分割する必要がある。しかし、分割するとデプロイ時に、テンプレートの数だけコマンドを叩く必要があるので、面倒。
そこでネストされたスタックを使用することで、親スタックをデプロイすれば依存関係のある子スタックもデプロイしてくれるので、とっても便利!
また、パラメータの変更など修正箇所を親テンプレートのみにできるので、エンハンスもしやすくなるなどのメリットもある。
#大まかな手順
####1.テンプレートファイルの作成
####2.子テンプレートをS3へアップロード
####3.子テンプレートのリンクを親テンプレートへ記述
####4.親テンプレートのスタックを作成
#テンプレート
.
├── root.yml
└── vpc.yml
└── ec2.yml
└── elb.yml
└── rds.yml
AWS::CloudFormation::Stack
ネストを使用する場合は、Typeに、AWS::CloudFormation::Stack
を指定する
Resources:
# VPCの作成
VPC:
Type: AWS::CloudFormation::Stack
Properties:
TemplateURL: !Ref TemplateVPC
Parameters:
AZ1: !Select [0, !GetAZs ""]
AZ2: !Select [1, !GetAZs ""]
CIDRBlock: "10.0.0.0/16"
PublicSubnet1CIDR: "10.0.1.0/24"
PublicSubnet2CIDR: "10.0.2.0/24"
PrivateSubnet1CIDR: "10.0.3.0/24"
PrivateSubnet2CIDR: "10.0.4.0/24"
Parameters:
に記載したパラメータは、子テンプレートに引き継いで使用できる
# パラメータ
Parameters:
AZ1:
Type: String
Description: Use AZ name
AZ2:
Type: String
Description: Use AZ name
CIDRBlock:
Type: String
Description: CIDRBlock of VPC
PublicSubnet1CIDR:
Type: String
Description: CIDRBlock of PublicSubnet1
PublicSubnet2CIDR:
Type: String
Description: CIDRBlock of PublicSubnet2
PrivateSubnet1CIDR:
Type: String
Description: CIDRBlock of PrivateSubnet1
PrivateSubnet2CIDR:
Type: String
Description: CIDRBlock of PrivateSubnet2
Resources:
# VPCの作成
VPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: !Ref CIDRBlock
InstanceTenancy: default
EnableDnsSupport: true
EnableDnsHostnames: true
Tags:
- Key: !FindInMap [Common, ServiceTag, Key]
Value: !FindInMap [Common, ServiceTag, Value]
Parameters:
に指定したkeyを、!Ref
で参照することができる
#子テンプレートをS3にアップロード
$ aws s3 mb yourbacket-name
$ aws s3 ls
#テンプレートファイルがあるディレクトリへ移動
$ cd currentDir
#カレントディレクトリにあるファイルを全てS3へアップロード
$ aws s3 cp . yourbacket-name --recursive
#子テンプレートのリンクをroot.ymlに記載
Parameters:
TemplateVPC:
Description: VPC template Object URL
Type: String
Default: https://~~~~/vpc.yml
#テンプレートファイルの構文チェック
$ aws cloudformation \
validate-template --template-body file://root.yml
yamlの構文に問題があれば、エラーとして表示される
#root.ymlをデプロイ
$ aws cloudformation \
create-stack --stack-name test-cfn \
--template-body file://root.yml
作成されたstackのidが返ってくる
スタックのアップデート
アップデート前に、changesetを作成すると、変更箇所が影響を及ぼすか確認することができる
アップデート時には、リソースが再起動する場合もあるので、注意が必要
$ aws cloudformation \
create-change-set --stack-name test-cfn \
--change-set-name fixup \
--template-body file://root.yml
$ aws cloudformation \
update-stack --stack-name test-cfn \
--template-body file://root.yml
#(参考)省略記法
#省略前
VpcId: {Ref: VPC}
#省略記法
VpcId: !Ref VPC
#(参考)組込関数
##!Ref
VpcId: !Ref VPC
- パラメータを指定した場合、設定した値
- リソースを指定した場合、リソースのID
が返り値となる
##!GetAtt
Outputs:
VPCId:
Value: !GetAtt VPC.Outputs.VPCId
Description: VPC CIDR Block
リソースごとに戻り値が異なるため、確認が必要
##!Base64
UserData: !Base64 |
#! /bin/bash
yum update -y
amazon-linux-extras install php7.2 -y
yum -y install mysql httpd php-mbstring php-xml
wget http://ja.wordpress.org/latest-ja.tar.gz -P /tmp/
tar zxvf /tmp/latest-ja.tar.gz -C /tmp
cp -r /tmp/wordpress/* /var/www/html/
touch /var/www/html/.check_alive
chown apache:apache -R /var/www/html
systemctl enable httpd.service
systemctl start httpd.service
入力文字列をBase64表現に変換する
#テンプレート全体
AWSTemplateFormatVersion: "2010-09-09"
Description: Manage myself verification environment
Parameters:
TemplateVPC:
Description: VPC template Object URL
Type: String
Default: https://~~~~/vpc.yml
TemplateEC2:
Description: EC2 template Object URL
Type: String
Default: https://~~~~/ec2.yml
TemplateELB:
Description: ELB template Object URL
Type: String
Default: https://~~~~/elb.yml
TemplateRDS:
Description: RDS template Object URL
Type: String
Default: https://~~~~/rds.yml
Resources:
# VPCの作成
VPC:
Type: AWS::CloudFormation::Stack
Properties:
TemplateURL: !Ref TemplateVPC
Parameters:
AZ1: !Select [0, !GetAZs ""]
AZ2: !Select [1, !GetAZs ""]
CIDRBlock: "10.0.0.0/16"
PublicSubnet1CIDR: "10.0.1.0/24"
PublicSubnet2CIDR: "10.0.2.0/24"
PrivateSubnet1CIDR: "10.0.3.0/24"
PrivateSubnet2CIDR: "10.0.4.0/24"
# EC2の作成
EC2:
Type: AWS::CloudFormation::Stack
Properties:
TemplateURL: !Ref TemplateEC2
Parameters:
VPCId: !GetAtt VPC.Outputs.VPCId
PublicSubnet1Id: !GetAtt VPC.Outputs.PublicSubnet1Id
PublicSubnet2Id: !GetAtt VPC.Outputs.PublicSubnet2Id
EC2AMI: /aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2
# VPC作成後に作成
DependsOn: VPC
# ELBの作成
ELB:
Type: AWS::CloudFormation::Stack
Properties:
TemplateURL: !Ref TemplateELB
Parameters:
VPCId: !GetAtt VPC.Outputs.VPCId
PublicSubnet1Id: !GetAtt VPC.Outputs.PublicSubnet1Id
PublicSubnet2Id: !GetAtt VPC.Outputs.PublicSubnet2Id
EC2WebServer01: !GetAtt EC2.Outputs.EC2WebServer01
EC2WebServer02: !GetAtt EC2.Outputs.EC2WebServer02
DependsOn: EC2
# RDSの作成
RDS:
Type: AWS::CloudFormation::Stack
Properties:
TemplateURL: !Ref TemplateRDS
Parameters:
VPCId: !GetAtt VPC.Outputs.VPCId
PrivateSubnet1Id: !GetAtt VPC.Outputs.PrivateSubnet1Id
PrivateSubnet2Id: !GetAtt VPC.Outputs.PrivateSubnet2Id
DBUser: dbmaster
DBPassword: H&ppyHands0n
DependsOn: VPC
# !GetAttは、テンプレートのリソースから属性の値を返す
Outputs:
VPCId:
Value: !GetAtt VPC.Outputs.VPCId
Description: VPC CIDR Block
PublicSubnet1Id:
Value: !GetAtt VPC.Outputs.PublicSubnet1Id
Description: PublicSubnet1 CIDR Block
PublicSubnet2Id:
Value: !GetAtt VPC.Outputs.PublicSubnet2Id
Description: PublicSubnet2 CIDR Block
PrivateSubnet1Id:
Value: !GetAtt VPC.Outputs.PrivateSubnet1Id
Description: PrivateSubnet1 CIDR Block
PrivateSubnet2Id:
Value: !GetAtt VPC.Outputs.PrivateSubnet2Id
Description: PrivateSubnet2 CIDR Block
EC2WebServer01:
Value: !GetAtt EC2.Outputs.EC2WebServer01
Description: EC2WebServer01 ID
EC2WebServer02:
Value: !GetAtt EC2.Outputs.EC2WebServer02
Description: EC2WebServer02 ID
DBEndpoint:
Value: !GetAtt RDS.Outputs.DBEndpoint
Description: MySQL Endpoint
FrontLBEndpoint:
Value: !GetAtt ELB.Outputs.FrontLBEndpoint
Description: ELB Endpoint
AWSTemplateFormatVersion: "2010-09-09"
Description: Provision of VPC
# tag
Mappings:
Common:
ServiceTag: {Key: Name, Value: dev}
# パラメータ
Parameters:
AZ1:
Type: String
Description: Use AZ name
AZ2:
Type: String
Description: Use AZ name
CIDRBlock:
Type: String
Description: CIDRBlock of VPC
PublicSubnet1CIDR:
Type: String
Description: CIDRBlock of PublicSubnet1
PublicSubnet2CIDR:
Type: String
Description: CIDRBlock of PublicSubnet2
PrivateSubnet1CIDR:
Type: String
Description: CIDRBlock of PrivateSubnet1
PrivateSubnet2CIDR:
Type: String
Description: CIDRBlock of PrivateSubnet2
Resources:
# VPCの作成
VPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: !Ref CIDRBlock
InstanceTenancy: default
EnableDnsSupport: true
EnableDnsHostnames: true
Tags:
- Key: !FindInMap [Common, ServiceTag, Key]
Value: !FindInMap [Common, ServiceTag, Value]
# IGWの作成
IGW:
Type: AWS::EC2::InternetGateway
DependsOn: VPC
Properties:
Tags:
- Key: !FindInMap [Common, ServiceTag, Key]
Value: !FindInMap [Common, ServiceTag, Value]
# IGWをVPCにアタッチ
IGWAttachment:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
VpcId: !Ref VPC
InternetGatewayId: !Ref IGW
# Public Subnetの作成
PublicSubnet1:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
AvailabilityZone: !Ref AZ1
CidrBlock: !Ref PublicSubnet1CIDR
MapPublicIpOnLaunch: true
Tags:
- Key: Name
Value: Public1-a
PublicSubnet2:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
AvailabilityZone: !Ref AZ2
CidrBlock: !Ref PublicSubnet2CIDR
MapPublicIpOnLaunch: true
Tags:
- Key: Name
Value: Public1-c
# Private Subnetの作成
PrivateSubnet1:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
AvailabilityZone: !Ref AZ1
CidrBlock: !Ref PrivateSubnet1CIDR
Tags:
- Key: Name
Value: Private1-a
PrivateSubnet2:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
AvailabilityZone: !Ref AZ2
CidrBlock: !Ref PrivateSubnet2CIDR
Tags:
- Key: Name
Value: Private1-c
# Public Route Table の作成と関連付け
PublicRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
DefaultPublicRoute:
Type: AWS::EC2::Route
DependsOn: IGWAttachment
Properties:
RouteTableId: !Ref PublicRouteTable
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref IGW
PublicSubnet1RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PublicRouteTable
SubnetId: !Ref PublicSubnet1
PublicSubnet2RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PublicRouteTable
SubnetId: !Ref PublicSubnet2
Outputs:
VPCId:
Value: !Ref VPC
Description: VPC id
PublicSubnet1Id:
Value: !Ref PublicSubnet1
Description: PublicSubnet1 id
PublicSubnet2Id:
Value: !Ref PublicSubnet2
Description: PublicSubnet2 id
PrivateSubnet1Id:
Value: !Ref PrivateSubnet1
Description: PrivateSubnet1 id
PrivateSubnet2Id:
Value: !Ref PrivateSubnet2
Description: PrivateSubnet2 id
AWSTemplateFormatVersion: "2010-09-09"
Description: Provision of EC2
# tag
Mappings:
Common:
ServiceTag: {Key: Name, Value: dev}
# パラメータ
Parameters:
VPCId:
Type: String
Description: ID for VPC.
PublicSubnet1Id:
Type: String
Description: ID for PublicSubnet1
PublicSubnet2Id:
Type: String
Description: ID for PublicSubnet2
EC2AMI:
Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>
Description: AMI
# EC2インスタンスの作成
Resources:
EC2WebServer01:
Type: AWS::EC2::Instance
Properties:
ImageId: !Ref EC2AMI
InstanceType: t2.micro
SubnetId: !Ref PublicSubnet1Id
UserData: !Base64 |
#! /bin/bash
yum update -y
amazon-linux-extras install php7.2 -y
yum -y install mysql httpd php-mbstring php-xml
wget http://ja.wordpress.org/latest-ja.tar.gz -P /tmp/
tar zxvf /tmp/latest-ja.tar.gz -C /tmp
cp -r /tmp/wordpress/* /var/www/html/
touch /var/www/html/.check_alive
chown apache:apache -R /var/www/html
systemctl enable httpd.service
systemctl start httpd.service
SecurityGroupIds:
- !Ref WEBSG
Tags:
- Key: !FindInMap [Common, ServiceTag, Key]
Value: !FindInMap [Common, ServiceTag, Value]
EC2WebServer02:
Type: AWS::EC2::Instance
Properties:
ImageId: !Ref EC2AMI
InstanceType: t2.micro
SubnetId: !Ref PublicSubnet2Id
UserData: !Base64 |
#! /bin/bash
yum update -y
amazon-linux-extras install php7.2 -y
yum -y install mysql httpd php-mbstring php-xml
wget http://ja.wordpress.org/latest-ja.tar.gz -P /tmp/
tar zxvf /tmp/latest-ja.tar.gz -C /tmp
cp -r /tmp/wordpress/* /var/www/html/
touch /var/www/html/.check_alive
chown apache:apache -R /var/www/html
systemctl enable httpd.service
systemctl start httpd.service
SecurityGroupIds:
- !Ref WEBSG
Tags:
- Key: !FindInMap [Common, ServiceTag, Key]
Value: !FindInMap [Common, ServiceTag, Value]
WEBSG:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: sg-web
VpcId: !Ref VPCId
SecurityGroupIngress:
# http
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: 10.0.0.0/16
# ssh
- IpProtocol: tcp
FromPort: 22
ToPort: 22
CidrIp: 10.0.0.0/16
Outputs:
EC2WebServer01:
Value: !Ref EC2WebServer01
Description: EC2WebServer01 id
EC2WebServer02:
Value: !Ref EC2WebServer02
Description: EC2WebServer02 id
AWSTemplateFormatVersion: "2010-09-09"
Description: Provision of ELB
# tag
Mappings:
Common:
ServiceTag: {Key: Name, Value: dev}
# パラメータ
Parameters:
VPCId:
Type: String
Description: ID for VPC.
PublicSubnet1Id:
Type: String
Description: ID for PublicSubnet1
PublicSubnet2Id:
Type: String
Description: ID for PublicSubnet2
EC2WebServer01:
Type: String
Description: EC2WebServer01 ID
EC2WebServer02:
Type: String
Description: EC2WebServer02 ID
# ロードバランサーの作成
Resources:
SecurityGroupLB:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: ELB-Web-SG
VpcId: !Ref VPCId
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: 0.0.0.0/0
FrontLB:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
Properties:
Name: elb-webserver
Subnets:
- !Ref PublicSubnet1Id
- !Ref PublicSubnet2Id
SecurityGroups:
- !Ref SecurityGroupLB
Type: application
Tags:
- Key: !FindInMap [Common, ServiceTag, Key]
Value: !FindInMap [Common, ServiceTag, Value]
FrontLBTargetGroup:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Properties:
Name: web-tg
VpcId: !Ref VPCId
# HealthCheck
HealthCheckIntervalSeconds: 30
# HealthCheckPath: /.check_alive
HealthCheckPath: '/'
HealthCheckPort: 80
HealthCheckProtocol: HTTP
HealthCheckTimeoutSeconds: 5
HealthyThresholdCount: 5
UnhealthyThresholdCount: 2
Matcher:
HttpCode: '200'
# Routing
# MEMO: トラフィックポートの場合は 'traffic-port'
Port: 80
Protocol: HTTP
TargetType: 'instance'
TargetGroupAttributes:
# ターゲットの登録解除までの待機時間
- Key: 'deregistration_delay.timeout_seconds'
Value: 300
Targets:
- Id: !Ref EC2WebServer01
Port: 80
- Id: !Ref EC2WebServer02
Port: 80
FrontLBListener:
Type: AWS::ElasticLoadBalancingV2::Listener
Properties:
LoadBalancerArn: !Ref FrontLB
Port: 80
Protocol: HTTP
DefaultActions:
- Type: forward
TargetGroupArn: !Ref FrontLBTargetGroup
Outputs:
FrontLBEndpoint:
Value: !GetAtt FrontLB.DNSName
Description: ELB Endpoint
AWSTemplateFormatVersion: "2010-09-09"
Description: Provision of RDS
# tag
Mappings:
Common:
ServiceTag: {Key: Name, Value: dev}
# パラメータ
Parameters:
VPCId:
Type: String
Description: ID for VPC.
PrivateSubnet1Id:
Type: String
Description: ID for PrivateSubnet1
PrivateSubnet2Id:
Type: String
Description: ID for PrivateSubnet1
DBUser:
Type: String
Description: DBUser ID
DBPassword:
Type: String
Description: DBUser Password
NoEcho: true
# RDSインスタンスの作成
Resources:
DBInstance:
Type: AWS::RDS::DBInstance
DeletionPolicy: Delete
Properties:
DBInstanceClass: db.t2.micro
AllocatedStorage: "10"
StorageType: gp2
Engine: MySQL
MultiAZ: True
MasterUsername: !Ref DBUser
MasterUserPassword: !Ref DBPassword
DBName: wordpress
BackupRetentionPeriod: 0
DBSubnetGroupName: !Ref DBSubnetGroup
VPCSecurityGroups:
- !Ref DBSecurityGroup
DBSubnetGroup:
Type: AWS::RDS::DBSubnetGroup
Properties:
DBSubnetGroupDescription: DB Subnet Group for Private Subnet
SubnetIds:
- !Ref PrivateSubnet1Id
- !Ref PrivateSubnet2Id
DBSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: sg-MySQL
VpcId: !Ref VPCId
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 3306
ToPort: 3306
CidrIp: 10.0.0.0/16
Outputs:
DBEndpoint:
Value: !GetAtt DBInstance.Endpoint.Address
Description: MySQL Endpoint
#最後に
上記、ファイルを使用すれば、エラーなくスタック作成できると思います。
細かいパラメータの設定などは、要件によって変更してください。
isuueなどあれば、お気軽にお願いします。