背景
先日案件でCloudFormationに触れる機会があり、備忘録も兼ねて簡単なAWS環境を作成してみました。
Cloud Formationとは
AWSの各種リソースをコードで管理できるサービスのことになります。一度コード化してしまえば同様のインフラ環境を何度も繰り返し作成することができるようになります。Cloud Formationのページでテンプレート(YAMlまたはJSONで作成する必要がある)と呼ばれるテキストファイルを読み込むことで、そこに記述されているAWSリソースを自動で作成してくれます。作成されたAWSリソースの料金はかかりますが、Cloud Formation自体の利用料金はかかりません。
作成した環境
今回はパブリックサブネットにEC2が1台、プライベートサブネットにRDSが1台作成される環境を作成してみました。
実際の業務を意識して以下の点を考慮して作成しました。
- 修正しやすいようにAWSリソースごとにyamlをなるべく分割
- EC2への外部からのアクセスはhttp通信のみ許可
- RDSの接続はEC2のみに制限
- RDSのパスワードはSecrets Managerで管理
作成したyamlファイル
ファイル名 | 作成されるAWSリソース |
---|---|
vpc.yaml | VPC,Internet Gateway |
network.yaml | Subnet,Route Table,Security Group |
iamrole.yaml | IAM Role |
ec2.yaml | EC2,Secrets Manager |
rds.yaml | RDS |
AWS構成図
vpc.yaml
AWSTemplateFormatVersion: 2010-09-09
Description: vpc
Parameters:
Env:
Type: String
Default: dev
AllowedValues:
- dev
- prod
Description: Environment for the VPC
SystemName:
Type: String
Default: test
Description: Name of the system
VpcCidr:
Type: String
Default: 10.1.0.0/16
Resources:
VPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: !Ref VpcCidr
EnableDnsSupport: true
EnableDnsHostnames: true
Tags:
- Key: Name
Value: !Sub "${SystemName}-${Env}-vpc"
InternetGateway:
Type: AWS::EC2::InternetGateway
Properties:
Tags:
- Key: Name
Value: !Sub "${SystemName}-${Env}-igw"
AttachInternetGateway:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
VpcId: !Ref VPC
InternetGatewayId: !Ref InternetGateway
Outputs:
VpcId:
Description: The ID of the VPC
Value: !Ref VPC
Export:
Name: !Sub "${AWS::StackName}-${Env}-VpcId"
InternetGatewayId:
Description: The ID of the Internet Gateway
Value: !Ref InternetGateway
Export:
Name: !Sub "${AWS::StackName}-${Env}-InternetGatewayId"
network.yaml
AWSTemplateFormatVersion: 2010-09-09
Description: subnet, security group
Parameters:
Env:
Type: String
Default: dev
AllowedValues:
- dev
- prod
Description: Environment for the subnet and security group
SystemName:
Type: String
Default: test
Description: Name of the system
VpcStackName:
Type: String
Default: test-vpc
Description: Name of the VPC stack to import
CidrBlockOfPublicSubnet:
Type: String
Default: 10.1.0.0/24
Description: CIDR block for the public subnet
CidrBlockOfPrivateSubnet1a:
Type: String
Default: 10.1.1.0/24
Description: CIDR block for the private subnet
CidrBlockOfPrivateSubnet1c:
Type: String
Default: 10.1.2.0/24
Description: CIDR block for the private subnet
Resources:
###########################subnet#############################
PublicSubnet:
Type: AWS::EC2::Subnet
Properties:
VpcId: !ImportValue
Fn::Sub: "${VpcStackName}-${Env}-VpcId"
CidrBlock: !Ref CidrBlockOfPublicSubnet
Tags:
- Key: Name
Value: !Sub "${SystemName}-${Env}-publicsubnet"
PrivateSubnet1a:
Type: AWS::EC2::Subnet
Properties:
VpcId: !ImportValue
Fn::Sub: "${VpcStackName}-${Env}-VpcId"
CidrBlock: !Ref CidrBlockOfPrivateSubnet1a
AvailabilityZone: ap-northeast-1a
Tags:
- Key: Name
Value: !Sub "${SystemName}-${Env}-privatesubnet1a"
PrivateSubnet1c:
Type: AWS::EC2::Subnet
Properties:
VpcId: !ImportValue
Fn::Sub: "${VpcStackName}-${Env}-VpcId"
CidrBlock: !Ref CidrBlockOfPrivateSubnet1c
AvailabilityZone: ap-northeast-1c
Tags:
- Key: Name
Value: !Sub "${SystemName}-${Env}-privatesubnet1c"
###########################route table########################
PublicRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !ImportValue
Fn::Sub: "${VpcStackName}-${Env}-VpcId"
Tags:
- Key: Name
Value: !Sub "${SystemName}-${Env}-public-routetable"
PrivateRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !ImportValue
Fn::Sub: "${VpcStackName}-${Env}-VpcId"
Tags:
- Key: Name
Value: !Sub "${SystemName}-${Env}-private-routetable"
PublicRoute:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref PublicRouteTable
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !ImportValue
Fn::Sub: "${VpcStackName}-${Env}-InternetGatewayId"
PublicSubnetRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PublicSubnet
RouteTableId: !Ref PublicRouteTable
PrivateSubnetRouteTableAssociation1a:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PrivateSubnet1a
RouteTableId: !Ref PrivateRouteTable
PrivateSubnetRouteTableAssociation1c:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PrivateSubnet1c
RouteTableId: !Ref PrivateRouteTable
############################security group######################
Ec2SecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
VpcId: !ImportValue
Fn::Sub: "${VpcStackName}-${Env}-VpcId"
GroupDescription: security group for the EC2
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: 0.0.0.0/0
Tags:
- Key: Name
Value: !Sub "${SystemName}-${Env}-public-securitygroup"
RdsSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
VpcId: !ImportValue
Fn::Sub: "${VpcStackName}-${Env}-VpcId"
GroupDescription: SecurityGroup for the RdsSecurityGroup
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 3306
ToPort: 3306
SourceSecurityGroupId: !Ref Ec2SecurityGroup
Tags:
- Key: Name
Value: !Sub "${SystemName}-${Env}-rds-securitygroup"
Outputs:
PublicSubnetId:
Description: The ID of the public subnet
Value: !Ref PublicSubnet
Export:
Name: !Sub "${AWS::StackName}-${Env}-PublicSubnetId"
PrivateSubnet1aId:
Description: The ID of the private subnet 1a
Value: !Ref PrivateSubnet1a
Export:
Name: !Sub "${AWS::StackName}-${Env}-PrivateSubnet1aId"
PrivateSubnet1cId:
Description: The ID of the private subnet 1c
Value: !Ref PrivateSubnet1c
Export:
Name: !Sub "${AWS::StackName}-${Env}-PrivateSubnet1cId"
Ec2SecurityGroupId:
Description: The ID of the EC2 security group
Value: !Ref Ec2SecurityGroup
Export:
Name: !Sub "${AWS::StackName}-${Env}-Ec2SecurityGroupId"
RdsSecurityGroupId:
Description: The ID of the RDS security group
Value: !Ref RdsSecurityGroup
Export:
Name: !Sub "${AWS::StackName}-${Env}-RdsSecurityGroupId"
iamrole.yaml
AWSTemplateFormatVersion: 2010-09-09
Description: vpc
Parameters:
Env:
Type: String
Default: dev
AllowedValues:
- dev
- prod
Description: Environment for the VPC
SystemName:
Type: String
Default: test
Description: Name of the system
Resources:
IamRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub "${SystemName}-${Env}-IamRole"
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: ec2.amazonaws.com
Action: sts:AssumeRole
Policies:
- PolicyName: !Sub "${SystemName}-${Env}-RdsPolicy"
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- rds:*
Resource: '*'
Tags:
- Key: Name
Value: !Sub "${SystemName}-${Env}-iamrole"
Outputs:
IamRoleName:
Description: The name of the IAM Role
Value: !Ref IamRole
Export:
Name: !Sub ${AWS::StackName}-${Env}-IamRoleName
ec2.yaml
AWSTemplateFormatVersion: 2010-09-09
Description: ec2, keypair
Parameters:
Env:
Type: String
Default: dev
AllowedValues:
- dev
- prod
Description: Environment for the VPC
SystemName:
Type: String
Default: test
Description: Name of the system
VpcStackName:
Type: String
Default: test-vpc
Description: Name of the VPC stack to import
NetWorkStackName:
Type: String
Default: test-network
Description: Name of the network stack to import
IamRoleStackName:
Type: String
Default: test-iamrole
Description: Name of the IAM role stack to import
Resources:
KeyPair:
Type: AWS::EC2::KeyPair
Properties:
KeyName: !Sub "${SystemName}-${Env}-keypair"
Tags:
- Key: Name
Value: !Sub "${SystemName}-${Env}-keypair"
InstanceProfile:
Type: AWS::IAM::InstanceProfile
Properties:
Path: /
Roles:
- !ImportValue
Fn::Sub: "${IamRoleStackName}-${Env}-IamRoleName"
Ec2Instance:
Type: AWS::EC2::Instance
Properties:
InstanceType: t2.micro
ImageId: ami-03598bf9d15814511
KeyName: !Ref KeyPair
NetworkInterfaces:
- AssociatePublicIpAddress: true
DeviceIndex: 0
SubnetId: !ImportValue
Fn::Sub: "${NetWorkStackName}-${Env}-PublicSubnetId"
GroupSet:
- !ImportValue
Fn::Sub: "${NetWorkStackName}-${Env}-Ec2SecurityGroupId"
IamInstanceProfile: !Ref InstanceProfile
UserData:
Fn::Base64: !Sub |
#!/bin/bash
sudo dnf update -y
sudo dnf install httpd -y
cd /var/www/html
sudo touch index.html
sudo echo '<html><h1>hallo world!</h1></html>' | sudo tee /var/www/html/index.html > /dev/null
sudo systemctl start httpd
sudo systemctl enable httpd
sudo sudo dnf install mariadb105 -y
Tags:
- Key: Name
Value: !Sub "${SystemName}-${Env}-ec2-instance"
Outputs:
KeyPairName:
Description: The name of the EC2 Key Pair
Value: !Ref KeyPair
Export:
Name: !Sub "${AWS::StackName}-${Env}-KeyPairName"
Ec2InstanceId:
Description: The ID of the EC2 instance
Value: !Ref Ec2Instance
Export:
Name: !Sub "${AWS::StackName}-${Env}-Ec2InstanceId"