SCSKの石原です。
最近、iPaaSの研修を受ける機会があり仮想マシンが複数台必要となったため、AWSにて研修用環境を用意しました。
簡単な構成ですが、NW周りの設定なども必要なため、1から作るとなると少し時間がかかるかと思います。
研修や検証などで、仮想マシンが欲しいときに当記事を活用いただけたらと思います。
研修用環境アーキテクチャ
下記の図の構成をCloudFormationを利用して作成します。この構成のポイントは下記の通りです。
- CFnテンプレートは、共用リソースと個別リソース(1ユーザ1サブネット想定)で分割しています。
- 仮想マシン(EC2)からはNatGWを介してアウトバウンド通信/S3エンドポイントへの通信/同一サブネット内の通信のみ許可されます。
- S3へのアクセスは、コスト最適化のためNatGWではなくS3エンドポイントを利用しています。
- 研修用環境を想定していますので、冗長化は全く考慮しておりません。
構築リソース
2つのCFnテンプレートを利用して構築します。
- 共通リソース
- 個別リソース
また、下記のテンプレートではS3の構成は実施しません。下記の記事などを参考に作成頂ければと思います。
S3 Intelligent-Tieringに自動的に移行されるバケットを作成する [AWS CloudFormation テンプレート付き]
今回はS3のコストを最適化してくれるストレージクラス「Intelligent-Tiering」に、自動的に移行してくれるバケットをCloudFormationでサクッと作れるテンプレートを用意しました。よりコストの安い「Archive Access tier 」と「Deep Archive Access tier」を利用するパターンも用意しています。ぜひご活用ください。
共有リソース
まずは共通リソース(VPCやNatGW・ルーティングなど)を作成します。
研修用途ですので、EC2に設定するロールに「AmazonS3FullAccess
」をアタッチしていますが、必要に応じて権限を変更ください。
AWSTemplateFormatVersion: "2010-09-09"
Description: "techharmony-traning-nw Template"
Parameters:
ProjectID:
Type: String
MinLength: 3
MaxLength: 15
Default: "th-traning"
AllowedValues:
- "th-traning"
Resources:
# ================================
# VPC
# ================================
SANDBOXVPC:
Type: "AWS::EC2::VPC"
Properties:
CidrBlock: "192.168.0.0/16"
EnableDnsSupport: true
EnableDnsHostnames: true
InstanceTenancy: "default"
Tags:
- Key: "Name"
Value: !Sub "${ProjectID}-vpc"
# ================================
# Subnet
# ================================
SubnetNW1a:
Type: "AWS::EC2::Subnet"
Properties:
AvailabilityZone: !Sub "${AWS::Region}a"
CidrBlock: "192.168.0.0/24"
VpcId: !Ref SANDBOXVPC
MapPublicIpOnLaunch: false
Tags:
- Key: "Name"
Value: !Sub "${ProjectID}-nw-public-1a"
# ================================
# ACL
# ================================
NetworkAclNW:
Type: "AWS::EC2::NetworkAcl"
Properties:
VpcId: !Ref SANDBOXVPC
Tags:
- Key: "Name"
Value: !Sub "${ProjectID}-nw-acl"
NetworkAclEC2:
Type: "AWS::EC2::NetworkAcl"
Properties:
VpcId: !Ref SANDBOXVPC
Tags:
- Key: "Name"
Value: !Sub "${ProjectID}-ec2-acl"
# # ACL association
NetworkAclAssociationNW1a:
Type: "AWS::EC2::SubnetNetworkAclAssociation"
Properties:
SubnetId: !Ref SubnetNW1a
NetworkAclId: !Ref NetworkAclNW
# # ACL NW Egress
NetworkAclEntryNWEg100:
Type: "AWS::EC2::NetworkAclEntry"
Properties:
CidrBlock: "0.0.0.0/0"
Egress: true
NetworkAclId: !Ref NetworkAclNW
Protocol: -1
RuleAction: "allow"
RuleNumber: 100
# # ACL NW Ingress
NetworkAclEntryNWIg100:
Type: "AWS::EC2::NetworkAclEntry"
Properties:
CidrBlock: "0.0.0.0/0"
Egress: false
NetworkAclId: !Ref NetworkAclNW
Protocol: -1
RuleAction: "allow"
RuleNumber: 100
# # ACL EC2 Egress
NetworkAclEntryEC2Eg100:
Type: "AWS::EC2::NetworkAclEntry"
Properties:
CidrBlock: "0.0.0.0/0"
Egress: true
NetworkAclId: !Ref NetworkAclEC2
Protocol: -1
RuleAction: "allow"
RuleNumber: 100
# # ACL EC2 Ingress
NetworkAclEntryEC2Ig100:
Type: "AWS::EC2::NetworkAclEntry"
Properties:
CidrBlock: "0.0.0.0/0"
Egress: false
NetworkAclId: !Ref NetworkAclEC2
Protocol: -1
RuleAction: "allow"
RuleNumber: 100
# ================================
# Route Table
# ================================
RouteTableNW:
Type: "AWS::EC2::RouteTable"
Properties:
VpcId: !Ref SANDBOXVPC
Tags:
- Key: "Name"
Value: !Sub "${ProjectID}-nw-public-rt"
RouteTableEC2:
Type: "AWS::EC2::RouteTable"
Properties:
VpcId: !Ref SANDBOXVPC
Tags:
- Key: "Name"
Value: !Sub "${ProjectID}-ec2-private-rt"
# route table association
RouteTableAssociationNW1a:
Type: "AWS::EC2::SubnetRouteTableAssociation"
Properties:
RouteTableId: !Ref RouteTableNW
SubnetId: !Ref SubnetNW1a
# ================================
# NAT Gateway
# ================================
InternetGateway:
Type: AWS::EC2::InternetGateway
Properties:
Tags:
- Key: stack
Value: "sandbox"
EIPNATGW:
Type: AWS::EC2::EIP
Properties:
Domain: vpc
NatGateway:
Type: AWS::EC2::NatGateway
Properties:
AllocationId: !GetAtt EIPNATGW.AllocationId
SubnetId: !Ref SubnetNW1a
VPCGatewayAttachment:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
InternetGatewayId: !Ref InternetGateway
VpcId: !Ref SANDBOXVPC
OutBoundOnlyRoute:
Type: AWS::EC2::Route
Properties:
DestinationCidrBlock: "0.0.0.0/0"
NatGatewayId: !Ref NatGateway
RouteTableId: !Ref RouteTableEC2
PublicRoute:
Type: AWS::EC2::Route
Properties:
DestinationCidrBlock: "0.0.0.0/0"
GatewayId: !Ref InternetGateway
RouteTableId: !Ref RouteTableNW
# ================================
# Endpoint(Gateway)
# ================================
s3Endpoint:
Type: "AWS::EC2::VPCEndpoint"
Properties:
VpcEndpointType: "Gateway"
VpcId: !Ref SANDBOXVPC
ServiceName: !Sub "com.amazonaws.${AWS::Region}.s3"
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal: "*"
Action: "*"
Resource: "*"
RouteTableIds:
- !Ref RouteTableEC2
PrivateDnsEnabled: false
# ================================
# IAM
# ================================
EC2IAMRole:
Type: "AWS::IAM::Role"
Properties:
Path: "/"
RoleName: !Sub "${ProjectID}-ec2-role"
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service:
- "ec2.amazonaws.com"
Action:
- "sts:AssumeRole"
MaxSessionDuration: 3600
ManagedPolicyArns:
- arn:aws:iam::aws:policy/AmazonS3FullAccess
- arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
Description: "Allows EC2 S3 and SSMCore"
EC2IAMInstanceProfile:
Type: 'AWS::IAM::InstanceProfile'
Properties:
Path: /
Roles:
- !Ref EC2IAMRole
Outputs:
ExportParamSANDBOXVPCID:
Description: SANDBOXVPCID
Value: !Ref SANDBOXVPC
Export:
Name: !Sub ${ProjectID}-SANDBOXVPC
ExportParamNetworkAclEC2:
Description: NetworkAclEC2
Value: !Ref NetworkAclEC2
Export:
Name: !Sub ${ProjectID}-NetworkAclEC2
ExportParamRouteTableEC2:
Description: RouteTableEC2
Value: !Ref RouteTableEC2
Export:
Name: !Sub ${ProjectID}-RouteTableEC2
ExportParamEC2IAMInstanceProfile:
Description: EC2IAMInstanceProfile
Value: !Ref EC2IAMInstanceProfile
Export:
Name: !Sub ${ProjectID}-EC2IAMInstanceProfile
個別リソース
共通リソースに仮想マシン(EC2)を2台デプロイするCFnテンプレートになります。
前提条件として、下記があります。
- 「
InstanceImage
」についてCFnテンプレートにamiIDをハードコーディングするか、パラメータストアに保存しておく必要があります。 - 「
EC2KeyPair
」について、事前に作成したキーペアをご入力ください。 - 「
ProjectID
」について、共通テンプレートで変更した場合は、こちらのテンプレートも修正ください。
また、共通リソースをクロススタック参照しています。テンプレートを修正して利用される場合はご注意ください。
AWSTemplateFormatVersion: "2010-09-09"
Description: "techharmony-traning-ec2 Template"
Parameters:
ProjectID:
Type: String
MinLength: 3
MaxLength: 15
Default: "th-traning"
AllowedValues:
- "th-traning"
# Must be unique
userID:
Type: String
MinLength: 3
MaxLength: 15
# 192.168.0.0/16
SubnetCidrBlock:
Type: String
Default: "192.168.1.0/24"
AllowedValues:
- "192.168.1.0/24"
- "192.168.2.0/24"
- "192.168.3.0/24"
- "192.168.4.0/24"
- "192.168.5.0/24"
- "192.168.6.0/24"
- "192.168.7.0/24"
- "192.168.8.0/24"
- "192.168.9.0/24"
- "192.168.10.0/24"
# Need to be created
EC2KeyPair:
Type: String
# Registration to the parameter store is required
InstanceImage:
Type: AWS::SSM::Parameter::Value
Default: /techharmony/sandbox/ami/windowsserver2019
SelectInstanceType:
Type: String
Default: t3.micro
AllowedValues:
- t3.micro
- t3.medium
- m5.large
- m5.xlarge
- m5.2xlarge
- m5.4xlarge
Resources:
# ================================
# Subnet
# ================================
SubnetEC21a:
Type: "AWS::EC2::Subnet"
Properties:
AvailabilityZone: !Sub "${AWS::Region}a"
CidrBlock: !Ref SubnetCidrBlock
VpcId: {'Fn::ImportValue': !Sub '${ProjectID}-SANDBOXVPC'}
MapPublicIpOnLaunch: false
Tags:
- Key: "Name"
Value: !Sub "${ProjectID}-${userID}-private-1a"
# ================================
# ACL Association
# ================================
NetworkAclAssociationEC21a:
Type: "AWS::EC2::SubnetNetworkAclAssociation"
Properties:
SubnetId: !Ref SubnetEC21a
NetworkAclId: {'Fn::ImportValue': !Sub '${ProjectID}-NetworkAclEC2'}
# ================================
# Route Association
# ================================
RouteTableAssociationEC21a:
Type: "AWS::EC2::SubnetRouteTableAssociation"
Properties:
RouteTableId: {'Fn::ImportValue': !Sub '${ProjectID}-RouteTableEC2'}
SubnetId: !Ref SubnetEC21a
# ================================
# EC2
# ================================
# Instance No 01
EC2Instance01:
Type: AWS::EC2::Instance
Properties:
AvailabilityZone: !Sub "${AWS::Region}a"
DisableApiTermination: false
EbsOptimized: false
IamInstanceProfile: {'Fn::ImportValue': !Sub '${ProjectID}-EC2IAMInstanceProfile'}
ImageId: !Ref InstanceImage
InstanceInitiatedShutdownBehavior: "stop"
InstanceType: !Ref SelectInstanceType
KeyName: !Ref EC2KeyPair
Monitoring: false
SecurityGroupIds:
- !Ref EC2SecurityGroup
SubnetId: !Ref SubnetEC21a
Tags:
- Key: "Name"
Value: !Sub "${ProjectID}-${userID}-ec2-01"
Tenancy: default
# Instance No 02
EC2Instance02:
Type: AWS::EC2::Instance
Properties:
AvailabilityZone: !Sub "${AWS::Region}a"
DisableApiTermination: false
EbsOptimized: false
IamInstanceProfile: {'Fn::ImportValue': !Sub '${ProjectID}-EC2IAMInstanceProfile'}
ImageId: !Ref InstanceImage
InstanceInitiatedShutdownBehavior: "stop"
InstanceType: !Ref SelectInstanceType
KeyName: !Ref EC2KeyPair
Monitoring: false
SecurityGroupIds:
- !Ref EC2SecurityGroup
SubnetId: !Ref SubnetEC21a
Tags:
- Key: "Name"
Value: !Sub "${ProjectID}-${userID}-ec2-02"
Tenancy: default
EC2SecurityGroup:
Type: "AWS::EC2::SecurityGroup"
Properties:
GroupDescription: !Sub "${ProjectID}-${userID}-ec2-sg"
GroupName: !Sub "${ProjectID}-${userID}-ec2-sg"
VpcId: {'Fn::ImportValue': !Sub '${ProjectID}-SANDBOXVPC'}
Tags:
- Key: "Name"
Value: !Sub "${ProjectID}-${userID}-ec2-sg"
SecurityGroupEC2Ingress01:
Type: AWS::EC2::SecurityGroupIngress
Properties:
CidrIp: !Ref SubnetCidrBlock
FromPort: 0
GroupId: !Ref EC2SecurityGroup
IpProtocol: -1
ToPort: 65535
Outputs:
InstanceIPAddress01:
Description: EC2 01 Instance IP address
Value: !GetAtt EC2Instance01.PrivateIp
InstanceIPAddress02:
Description: EC2 02 Instance IP address
Value: !GetAtt EC2Instance02.PrivateIp
終わりに
管理者が環境を払い出す仕掛けにするのであれば、このままCFnテンプレートでスタックを作成いただけます。
セルフサービスで社内のユーザに活用してもらうのであれば、「AWS Service Catalog」に登録しておくのも良いかと思います。
また、性能の高いインスタンスの放置にはお気を付けください。
なにかしらお役にたてていれば幸いです。