はじめに
AWS CloudFormationを利用してVPC構築のテンプレートのサンプルです。
テンプレートの概要が分からない場合は、はじめてのAWS CloudFormationテンプレートを理解するを参考にしてください。
コードはGitHubにもあります。
今回は、akane というシステムの all 環境を想定しています。
同じ構成で違う環境を作成する場合は、{環境名}-parameters.jsonを別途作成します。
ディレクトリ構成
akane-cfn (システム)
├─ network (スタック)
| ├─ network.yml (CFnテンプレート)
| └─ all-parameters.json (all 環境のパラメータ)
├─ instance-cfn (スタック)
| ├─ instance-cfn.yml (CFnテンプレート)
| └─ all-parameters.json (all 環境のパラメータ)
└─ endpoint-cfn (スタック)
├─ endpoint-cfn.yml (CFnテンプレート)
└─ all-parameters.json (all 環境のパラメータ)
AWS リソース構築内容
-
networkスタック
- VPC (10.0.0.0/16)
- Publicサブネット (10.0.1.0/24)
- インターネットゲートウェイ
- Publicルートテーブル
- Elastic IP (NATゲートウェイ用)
-
instance-cfnスタック
- セキュリティグループ (インスタンス)
- IAMロール (AdministratorAccess)
- インスタンスプロファイル
- EC2インスタンス (Amazon Linux 2)
-
endpoint-cfnスタック
- セキュリティグループ (エンドポイント:アウトバウンドにインスタンスのセキュリティグループを指定)
- セキュリティグループのアウトバウンド (インスタンス:アウトバウンドにエンドポイントのセキュリティグループを指定)
- CloudFormationエンドポイント (Interface)
実行環境の準備
AWS CloudFormationを動かすためのAWS CLIの設定を参考にしてください。
AWS リソース構築手順
-
事前にEC2のキーペアを作成し、
akane-cfn/instance-cfn/all-parameters.json
のKeyName
に記載します。 -
下記を実行してスタックを作成
./create_stacks.sh
-
下記を実行してスタックを削除
./delete_stacks.sh
構築テンプレート
1. networkスタック
network.yml
AWSTemplateFormatVersion: 2010-09-09
Description: Network For Akane
# Metadata:
Parameters:
SystemName:
Type: String
AllowedPattern: '[a-zA-Z0-9-]*'
EnvType:
Description: Environment type.
Type: String
AllowedValues: [all, dev, stg, prod]
ConstraintDescription: must specify all, dev, stg, or prod.
VPCCidrBlock:
Type: String
AllowedPattern: (\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})/16
PublicSubnet:
Type: String
AllowedPattern: (\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})/24
Mappings:
AzMap:
ap-northeast-1:
1st: ap-northeast-1a
2nd: ap-northeast-1c
3rd: ap-northeast-1d
# Conditions
# Transform
Resources:
# VPC作成
akaneVPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: !Ref VPCCidrBlock
EnableDnsSupport: true
EnableDnsHostnames: true
InstanceTenancy: default
Tags:
- Key: Name
Value: !Sub
- ${SystemName}-${EnvType}-vpc
- {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
- Key: SystemName
Value: !Ref SystemName
- Key: EnvType
Value: !Ref EnvType
###########################################################################
# Publicサブネット関連
###########################################################################
# Publicサブネット作成
akanePublicSubnet:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref akaneVPC
CidrBlock: !Ref PublicSubnet
AvailabilityZone: !FindInMap [AzMap, !Ref AWS::Region, 1st]
MapPublicIpOnLaunch: true
Tags:
- Key: Name
Value: !Sub
- ${SystemName}-${EnvType}-public-subnet
- {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
- Key: SystemName
Value: !Ref SystemName
- Key: EnvType
Value: !Ref EnvType
# インターネットゲートウェイ作成
akaneInternetGateway:
Type: AWS::EC2::InternetGateway
Properties:
Tags:
- Key: Name
Value: !Sub
- ${SystemName}-${EnvType}-igw
- {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
- Key: SystemName
Value: !Ref SystemName
- Key: EnvType
Value: !Ref EnvType
# インターネットゲートウェイをVPCにアタッチ
akaneVPCGatewayAttachment:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
VpcId: !Ref akaneVPC
InternetGatewayId: !Ref akaneInternetGateway
# Publicルートテーブル作成
akanePublicRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref akaneVPC
Tags:
- Key: Name
Value: !Sub
- ${SystemName}-${EnvType}-public-route
- {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
- Key: SystemName
Value: !Ref SystemName
- Key: EnvType
Value: !Ref EnvType
# Publicルートテーブル サブネット関連付け
akanePublicSubnetRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref akanePublicSubnet
RouteTableId: !Ref akanePublicRouteTable
# Publicルートテーブル ルートにインターネットゲートウェイを関連付ける
akanePublicRoute:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref akanePublicRouteTable
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref akaneInternetGateway
Outputs:
akaneVPC:
Value: !Ref akaneVPC
Export:
Name: !Sub
- ${SystemName}-${EnvType}-vpc
- {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
akanePublicSubnet:
Value: !Ref akanePublicSubnet
Export:
Name: !Sub
- ${SystemName}-${EnvType}-public-subnet
- {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
akanePublicRouteTable:
Value: !Ref akanePublicRouteTable
Export:
Name: !Sub
- ${SystemName}-${EnvType}-public-route-table
- {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
all-parameters.json
{
"Parameters": [
{
"ParameterKey": "SystemName",
"ParameterValue": "akane-cfn"
},
{
"ParameterKey": "EnvType",
"ParameterValue": "all"
},
{
"ParameterKey": "VPCCidrBlock",
"ParameterValue": "10.10.0.0/16"
},
{
"ParameterKey": "PublicSubnet",
"ParameterValue": "10.10.1.0/24"
}
]
}
2. EC2インスタススタック
instance-cfn.yml
AWSTemplateFormatVersion: 2010-09-09
Description: Instance For Akane
# Metadata:
Parameters:
SystemName:
Type: String
AllowedPattern: '[a-zA-Z0-9-]*'
EnvType:
Description: Environment type.
Type: String
AllowedValues: [all, dev, stg, prod]
ConstraintDescription: must specify all, dev, stg, or prod.
ImageId:
Type: String
AllowedPattern: 'ami-[a-zA-Z0-9-]*'
InstanceType:
Type: String
AllowedPattern: '^[a-z][1-9][.][a-z0-9]+$'
KeyName:
Type: String
Mappings:
AzMap:
ap-northeast-1:
1st: ap-northeast-1a
2nd: ap-northeast-1c
3rd: ap-northeast-1d
# Conditions
# Transform
Resources:
# セキュリティグループ(EC2インスタンス)作成
akaneSecurityGroupInstance:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: !Sub
- ${SystemName}-${EnvType}-sg-instance
- {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
GroupName: !Sub
- ${SystemName}-${EnvType}-sg-instance
- {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 22
ToPort: 22
CidrIp: 0.0.0.0/0
SecurityGroupEgress:
- IpProtocol: tcp
FromPort: 0
ToPort: 65535
CidrIp: 0.0.0.0/0
Tags:
- Key: Name
Value: !Sub
- ${SystemName}-${EnvType}-sg-instance
- {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
- Key: SystemName
Value: !Ref SystemName
- Key: EnvType
Value: !Ref EnvType
VpcId:
Fn::ImportValue: !Sub
- ${SystemName}-${EnvType}-vpc
- {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
# IAMロールを作成
akaneRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- ec2.amazonaws.com
Action:
- sts:AssumeRole
Description: !Sub
- ${SystemName}-${EnvType}-role
- {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
ManagedPolicyArns:
- arn:aws:iam::aws:policy/AdministratorAccess
RoleName: !Sub
- ${SystemName}-${EnvType}-role
- {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
Tags:
- Key: Name
Value: !Sub
- ${SystemName}-${EnvType}-role
- {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
- Key: SystemName
Value: !Ref SystemName
- Key: EnvType
Value: !Ref EnvType
# インスタンスプロファイル作成
akaneInstanceProfile:
Type: AWS::IAM::InstanceProfile
Properties:
Path: "/"
Roles:
- !Ref akaneRole
# EC2インスタンス作成
akaneInstance:
Type: AWS::EC2::Instance
Properties:
AvailabilityZone: !FindInMap [AzMap, !Ref AWS::Region, 1st]
DisableApiTermination: false
IamInstanceProfile:
!Ref akaneInstanceProfile
ImageId: !Ref ImageId
InstanceInitiatedShutdownBehavior: stop
InstanceType: !Ref InstanceType
KeyName: !Ref KeyName
Monitoring: false
SecurityGroupIds:
- !Ref akaneSecurityGroupInstance
SubnetId:
Fn::ImportValue: !Sub
- ${SystemName}-${EnvType}-public-subnet
- {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
Tags:
- Key: Name
Value: !Sub
- ${SystemName}-${EnvType}-instance
- {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
- Key: SystemName
Value: !Ref SystemName
- Key: EnvType
Value: !Ref EnvType
Tenancy: default
UserData:
Fn::Base64: !Sub |
#!/bin/bash
yum update -y
yum install git -y
cd /home/ec2-user
# ec2-user bashrc設定
cat <<EOS | sudo tee -a /home/ec2-user/.bashrc
SERVERNAME=${SystemName}-${EnvType}
export PS1='\[\e[1;36m\][\e[1;33m\]\u@\[\e[1;32m\]\${!SERVERNAME} \w\[\e[1;36m\]]$\[\e[00m\] '
export AWS_DEFAULT_REGION=${AWS::Region}
EOS
# root bashrc設定
cat <<EOS | sudo tee /root/.bashrc
export PS1="\[\e[1;36m\][\u@\h \W]\$\[\e[00m\] "
EOS
# EIP作成およびEC2インスタンスへのアタッチ
akaneEIP:
Type: AWS::EC2::EIP
Properties:
Domain: vpc
InstanceId: !Ref akaneInstance
Tags:
- Key: Name
Value: !Sub
- ${SystemName}-${EnvType}-eip
- {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
- Key: SystemName
Value: !Ref SystemName
- Key: EnvType
Value: !Ref EnvType
Outputs:
akaneSecurityGroupInstance:
Value: !Ref akaneSecurityGroupInstance
Export:
Name: !Sub
- ${SystemName}-${EnvType}-sg-instance
- {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
all-parameters.json
{
"Parameters": [
{
"ParameterKey": "SystemName",
"ParameterValue": "akane-cfn"
},
{
"ParameterKey": "EnvType",
"ParameterValue": "all"
},
{
"ParameterKey": "ImageId",
"ParameterValue": "ami-0ce107ae7af2e92b5"
},
{
"ParameterKey": "InstanceType",
"ParameterValue": "t2.micro"
},
{
"ParameterKey": "KeyName",
"ParameterValue": "akane"
}
],
"Capabilities": [
"CAPABILITY_NAMED_IAM"
]
}
3. CloudFormationエンドポイントスタック
instance-cfn.yml
AWSTemplateFormatVersion: 2010-09-09
Description: Endpoint For Akane
# Metadata:
Parameters:
SystemName:
Type: String
AllowedPattern: '[a-zA-Z0-9-]*'
EnvType:
Description: Environment type.
Type: String
AllowedValues: [all, dev, stg, prod]
ConstraintDescription: must specify all, dev, stg, or prod.
Mappings:
AzMap:
ap-northeast-1:
1st: ap-northeast-1a
2nd: ap-northeast-1c
3rd: ap-northeast-1d
# Conditions
# Transform
Resources:
# セキュリティグループ(エンドポイント)作成
akaneSecurityGroupEndpoint:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: !Sub
- ${SystemName}-${EnvType}-sg-endpoint-cfn
- {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
GroupName: !Sub
- ${SystemName}-${EnvType}-sg-endpoint-cfn
- {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 0
ToPort: 65535
SourceSecurityGroupId:
Fn::ImportValue: !Sub
- ${SystemName}-${EnvType}-sg-instance
- {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
Tags:
- Key: Name
Value: !Sub
- ${SystemName}-${EnvType}-sg-endpoint-cfn
- {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
- Key: SystemName
Value: !Ref SystemName
- Key: EnvType
Value: !Ref EnvType
VpcId:
Fn::ImportValue: !Sub
- ${SystemName}-${EnvType}-vpc
- {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
#VPCエンドポイント作成
akaneCloudFormationEndpoint:
Type: AWS::EC2::VPCEndpoint
Properties:
PrivateDnsEnabled: true
SecurityGroupIds:
- !Ref akaneSecurityGroupEndpoint
ServiceName: !Sub com.amazonaws.${AWS::Region}.cloudformation
SubnetIds:
- Fn::ImportValue: !Sub
- ${SystemName}-${EnvType}-public-subnet
- {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
VpcEndpointType: Interface
VpcId:
Fn::ImportValue: !Sub
- ${SystemName}-${EnvType}-vpc
- {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
# セキュリティグループ(EC2インスタンス)のアウトバウンドルール作成
akaneSecurityGroupEgressInstance:
Type: AWS::EC2::SecurityGroupEgress
Properties:
Description: !Sub
- ${SystemName}-${EnvType}-sg-instance-cfn
- {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
DestinationSecurityGroupId: !Ref akaneSecurityGroupEndpoint
IpProtocol: -1
GroupId:
Fn::ImportValue: !Sub
- ${SystemName}-${EnvType}-sg-instance
- {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
# Outputs:
all-parameters.json
{
"Parameters": [
{
"ParameterKey": "SystemName",
"ParameterValue": "akane-cfn"
},
{
"ParameterKey": "EnvType",
"ParameterValue": "all"
}
]
}
4. 実行ファイル
create_stacks.sh
#!/bin/sh
SYSTEM_NAME=akane-cfn
ENV_TYPE=all
create_stack () {
aws cloudformation create-stack \
--stack-name ${SYSTEM_NAME}-${ENV_TYPE}-${STACK_NAME} \
--template-body file://./${SYSTEM_NAME}/${STACK_NAME}/${STACK_NAME}.yml \
--cli-input-json file://./${SYSTEM_NAME}/${STACK_NAME}/${ENV_TYPE}-parameters.json \
--capabilities CAPABILITY_NAMED_IAM
aws cloudformation wait stack-create-complete \
--stack-name ${SYSTEM_NAME}-${ENV_TYPE}-${STACK_NAME}
}
create_stack network
create_stack instance-cfn '--capabilities CAPABILITY_NAMED_IAM'
create_stack endpoint-cfn
delete_stacks.sh
#!/bin/sh
SYSTEM_NAME=akane-cfn
ENV_TYPE=all
delete_stack () {
STACK_NAME=$1
aws cloudformation delete-stack \
--stack-name ${SYSTEM_NAME}-${ENV_TYPE}-${STACK_NAME}
aws cloudformation wait stack-delete-complete \
--stack-name ${SYSTEM_NAME}-${ENV_TYPE}-${STACK_NAME}
}
delete_stack endpoint-cfn
delete_stack instance-cfn
delete_stack network