この記事は、株式会社日立システムズ Advent Calendar 2022の21日目の記事です。
第1回では、CloudFormationの概要についてまとめました。
第2回の今回は実際に簡単なCloudFormationテンプレートを作成し、内容を説明していきたいと思います。
CloudFormationテンプレートの説明
1. 作成するAWS環境
今回作成するAWS環境の構成は以下の通りです。
シングルAZにPublicSubnetを作成し、EC2インスタンスを立ち上げるという簡単な構成です。
2. 作成したCloudFormationテンプレート
実際に作成したテンプレートは以下の通りです。
今回はYAML形式で作成しています。
AWSTemplateFormatVersion: "2010-09-09"
Description: "Qiita Stack"
# --------------------------------------------------------------------------------------------------------------
# Input Parameters
# --------------------------------------------------------------------------------------------------------------
Parameters:
PjName:
Type: String
Default: Qiita
Description: Project Name
Env:
Type: String
Default: dev
Description: Environment Name
VpcCidr:
Type: String
Default: 192.168.0.0/16
Description: Cidr of Vpc
PublicSubnetCidr:
Type: String
Default: 192.168.0.0/24
Description: Cidr of PublicSubnet
VolumeSize01:
Type: String
Default: 8
Description: VolumeSize of PublicSubnetEc2
ImageId01:
Type: AWS::EC2::Image::Id
Default: ami-0de5311b2a443fb89
Description: ImageId of PublicSubnetEc2
InstanceType01:
Type: String
Default: t3.micro
Description: InstanceType of PublicSubnetEc2
SourceIp:
Type: String
Default: 10.0.0.0/8
Description: SourceIp of Internet
Resources:
# --------------------------------------------------------------------------------------------------------------
# Title: VPC
# Description: VPC of Qiita-dev
# --------------------------------------------------------------------------------------------------------------
Vpc:
Type: AWS::EC2::VPC
Properties:
CidrBlock: !Ref VpcCidr
Tags:
- Key: Name
Value: !Sub ${PjName}-${Env}-VPC
# --------------------------------------------------------------------------------------------------------------
# Title: InternetGateway
# Description: InternetGateway of Qiita-dev-VPC
# --------------------------------------------------------------------------------------------------------------
# InternetGateway
InternetGateway:
Type: AWS::EC2::InternetGateway
Properties:
Tags:
- Key: Name
Value: !Sub ${PjName}-${Env}-IGW
# Attach InternetGateway
InternetGatewayAttachment:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
VpcId: !Ref Vpc
InternetGatewayId: !Ref InternetGateway
# --------------------------------------------------------------------------------------------------------------
# Title: Subnet
# Description: PublicSubnet of Qiita-dev-VPC
# --------------------------------------------------------------------------------------------------------------
# PublicSubnet
PublicSubnet:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone:
Fn::Select:
- 0
- Fn::GetAZs: !Ref AWS::Region
CidrBlock: !Ref PublicSubnetCidr
Tags:
- Key: Name
Value: !Sub ${PjName}-${Env}-PublicSN
VpcId: !Ref Vpc
# --------------------------------------------------------------------------------------------------------------
# Title: RouteTable
# Description: RouteTable,Route,SubnetRouteTableAssociation of Qiita-dev-PublicSN
# --------------------------------------------------------------------------------------------------------------
# RouteTable of PublicSubnet
PublicSubnetRouteTable:
Type: AWS::EC2::RouteTable
Properties:
Tags:
- Key: Name
Value: !Sub ${PjName}-${Env}-PublicSN-RT
VpcId: !Ref Vpc
# Route of PublicSubnetRouteTable
PublicSubnetRoute:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref PublicSubnetRouteTable
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref InternetGateway
# Subnet Route Assocciation of PublicSubnetRouteTable
PublicSubnetSRTAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PublicSubnetRouteTable
SubnetId: !Ref PublicSubnet
# --------------------------------------------------------------------------------------------------------------
# Title: EC2
# Description: EC2 of Qiita-dev-PublicSN
# --------------------------------------------------------------------------------------------------------------
# EC2 of PublicSubnet
PublicSubnetEc2:
Type: AWS::EC2::Instance
Properties:
BlockDeviceMappings:
- DeviceName: /dev/xvda
Ebs:
VolumeType: gp2
VolumeSize: !Ref VolumeSize01
EbsOptimized: true
ImageId: !Ref ImageId01
InstanceType: !Ref InstanceType01
KeyName: !Ref PublicSubnetEc2Key
SecurityGroupIds:
- !Ref Ec2SecurityGroup
SubnetId: !Ref PublicSubnet
Tags:
- Key: Name
Value: !Sub ${PjName}-${Env}-EC2
IamInstanceProfile: !Ref Ec2InstanceProfile
# --------------------------------------------------------------------------------------------------------------
# Title: SecurityGroup
# Description: SecurityGroup of Qiita-dev-EC2
# --------------------------------------------------------------------------------------------------------------
# SecurityGroup of PublicSubnetEc2
Ec2SecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Security Group of PublicSubnet EC2 Instance
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: !Ref SourceIp
Tags:
- Key: Name
Value: !Sub ${PjName}-${Env}-EC2-SG
VpcId: !Ref Vpc
# --------------------------------------------------------------------------------------------------------------
# Title: IAM Role
# Description: IAM Role,InstanceProfile of Qiita-dev-EC2
# --------------------------------------------------------------------------------------------------------------
# IAM Role of PublicSubnetEc2
Ec2Role:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub ${PjName}-${Env}-EC2-Role
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service:
- ec2.amazonaws.com
Action:
- sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
# InstanceProfile of PublicSubnetEc2
Ec2InstanceProfile:
Type: AWS::IAM::InstanceProfile
Properties:
Roles:
- !Ref Ec2Role
# --------------------------------------------------------------------------------------------------------------
# Title: Key Pair
# Description: Key Pair of Qiita-dev-EC2
# --------------------------------------------------------------------------------------------------------------
# KeyPair of PublicSubnetEc2
PublicSubnetEc2Key:
Type: AWS::EC2::KeyPair
Properties:
KeyName: !Sub ${PjName}-${Env}-EC2-Key
各セクションごとに説明します。
2.1 AWSTemplateFormatVersion・Description
AWSTemplateFormatVersion: "2010-09-09"
Description: "Qiita Stack"
AWSTemplateFormatVersionでは
最新のテンプレートの形式バージョン「2010-09-09」を記述しています。
Descriptionでは
今回のテンプレートに関して、説明文を記述しています。
2.2 Parameters
Parameters:
PjName:
Type: String
Default: Qiita
Description: Project Name
Env:
Type: String
Default: dev
Description: Environment Name
VpcCidr:
Type: String
Default: 192.168.0.0/16
Description: Cidr of Vpc
PublicSubnetCidr:
Type: String
Default: 192.168.0.0/24
Description: Cidr of PublicSubnet
VolumeSize01:
Type: String
Default: 8
Description: VolumeSize of PublicSubnetEc2
ImageId01:
Type: AWS::EC2::Image::Id
Default: ami-0de5311b2a443fb89
Description: ImageId of PublicSubnetEc2
InstanceType01:
Type: String
Default: t3.micro
Description: InstanceType of PublicSubnetEc2
SourceIp:
Type: String
Default: 10.0.0.0/8
Description: SourceIp of Internet
プロジェクト名や環境名からVPCやSubnetで設定するCIDRやEC2で設定するAMIID等を記述することでスタックを作成または更新時にGUI上でパラメータとして入力できるようになります。
2.3 Resources
実際に構築したいリソースを記述しています。
今回は上から
- VPC
- InternetGateway
- Subnet
- RouteTable(宛先InternetGatewayへのデフォルトルートを設定)
- EC2
- SecurityGroup(特定送信元IPからのポート80に対するインバウンド通信を許可)
- IAMRole(EC2でセッションマネージャーを使用できるようIAMPolicy「AmazonSSMManagedInstanceCore」を設定)
- KeyPair(EC2で使用するキーペアを作成)
の作成に必要な内容を記述しています。
記述した各リソースの詳細に関しては以下を参照ください。
また、Resources内で組み込み関数を使用しています。
今回使用している組み込み関数に関して説明します。以下の通りです。
# | 組み込み関数名(短縮形) | 説明 |
---|---|---|
1 | Fn::GetAZs(!GetAZs) | 指定したリージョンのアベイラビリティーゾーンの配列を返す。例)[ap-northeast-1a、ap-northeast-1c、ap-northeast-1d] |
2 | Fn::Select(!Select) | リストから値を取得する。 |
3 | Fn::Sub(!Sub) | 指定した変数の値に置き換える。 |
4 | Ref(!Ref) | 指定したパラメータまたはリソースの値を返す。 |
では、「PublicSubnet」を参考に組み込み関数の内容を確認します。
# PublicSubnet
PublicSubnet:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone:
Fn::Select:
- 0
- Fn::GetAZs: !Ref AWS::Region
CidrBlock: !Ref PublicSubnetCidr
Tags:
- Key: Name
Value: !Sub ${PjName}-${Env}-PublicSN
VpcId: !Ref Vpc
まず、Properties の「AvailabilityZone」ではSubnetを作成するAZを
「ap-northeast-1a」のように値を指定する必要があります。
今回はFn::Selectを使用し、Fn::GetAZsで取得したアベイラビリティーゾーンの配列から0番目の値、すなわち「ap-northeast-1a」を返すように記載しています。
(※今回のリソースを作成するリージョンはap-northeast-1)
なお、Fn::GetAZsでは「AWS::Region」をRefで参照していますが、
CloudFormationでは「AWS::Region」のように事前定義されたパラメータ(擬似パラメータ)がいくつか用意されています。
このようなパラメータはParametersで記述する必要がありません。
ちなみにAWS::Regionでは包括的なリソースが作成されているリージョンを表す文字列を返します。
次に「Tags」ではSubnetに割り当てるタグを指定できます。
例えば「Key:Name」に対し、
「Value:(プロジェクト名)-(環境名)-(リソース名)」という命名規則があった際にFn::Subで変数を指定することでプロジェクト名や環境名を都度入力せずに済みます。
また、Parametersで値を変更することで全てのタグのValue値を動的に変更できますので汎用性の高いテンプレートを作成することができます。
次に「VpcId」ではSubnetを作成するVPCのIDを指定する必要があります。
ただ、スタックを作成するまでVpcIdは判明しませんので、以上のようにRefを使用することで値を指定(参照)できます。
ここでSubnet がVpcIDを使用するためにスタックを作成する際はVPC→Subnetの順番で作成される必要があります。
ただ組み込み関数 Refを使用していると暗黙的に参照先のリソースから作成するようになります。
Properties内でリソース属性の中のDependsOn属性を利用し、明示的にリソースの作成順番を指定することもできますので詳細は以下を参照してみてください。
その他のリソース属性に関しては以下を参照してみてください。
まとめ
今回は簡単なAWS環境を基にCloudFormationテンプレートを作成し、内容を説明してきました。
各セクションで記述する内容や組み込み関数等について理解いただけたと思います。
ただ、今回は簡単なAWS環境を想定し作成しましたが、実際の環境は多くのリソースが構築されていると思います。
その際に全てのリソースを1つのテンプレートで記載していくと、CloudFormationを利用するメリットが損なわれてしまう可能性があります。
そこで次回は、今回作成したテンプレートを改善すると同時に「ネストスタック」の考え方について紹介したいと思います。
※本記事は2022/12/13時点での内容になります。
最新の情報に関しては公式ドキュメントを参照ください。