CFnでCloudFrontDistribution+ALB+EC2その他を作成。
今回はテンプレートが長いので
手順について詳しく書くと読む側もキツイはずなので概要だけ纏める形にしました。
【今回の構成その他】
・VPC内で2AZそれぞれにパブリックサブネット、プライベートサブネットを作成。
・両パブリックサブネットにALB紐付け、同両パプリックサブネットにEIPをアタッチしたNATゲートウェイ配置。
・EC2は両プライベートサブネットに配置。
・VPC外にはCloudFrontDistributionを作成。
【その他】
・カスタムヘッダーを設定し、CloudFrontのDomainからのアクセス時のみ表示に限定【参考にした記事】
・ALBのEgressとEC2のIngressを相互参照するSecurityGroupの書き方に挑戦。【参考にした記事】
・リソースのNameTagのValueには「!Sub ${AWS::StackName}-」を利用。
・EC2にはセッションマネージャーでアクセス出来るようSSMmanagedInstanceCoreのRoleと4つのVPCE(更新用のS3GatewayTypeも含む)、443のSecurityGroupを作成。
・今回はACMなど無しなのでポートは全て80。
・出力(Outputs)にアクセス可否確認用のCloudFrontとALBのDNSを追加。
Yamlテンプレート内容
※長いので開閉式にしました。
sample.yml
AWSTemplateFormatVersion: 2010-09-09
Description: for practice
# ============== パラメータ ==============
# メタデータ
Metadata:
AWS::CloudFormation::Interface:
ParameterGroups:
-
Label:
default: Paramerters
Parameters:
- MyCustomHeaderString
# パラメータ
Parameters:
MyCustomHeaderString:
Type: String
Description: Enter any min4/max16 length characters for custom header value. It must begin with a letter and contain only alphanumeric.
MinLength: 4
MaxLength: 16
AllowedPattern: "[a-zA-Z][a-zA-Z0-9]*"
# ============== リソース ==============
Resources:
# -------------- IAM --------------
# ロール
MySSMMICRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service:
- ec2.amazonaws.com
Action:
- sts:AssumeRole
Path: /
RoleName: !Sub ${AWS::StackName}-MySSMMICRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
# インスタンスプロファイル
MyInstanceProfile:
Type: AWS::IAM::InstanceProfile
Properties:
Path: /
Roles:
- !Ref MySSMMICRole
# -------------- クラウドフロント --------------
MyCloudFront:
Type: AWS::CloudFront::Distribution
Properties:
DistributionConfig:
DefaultCacheBehavior:
AllowedMethods:
- GET
- HEAD
CachedMethods:
- GET
- HEAD
CachePolicyId: 658327ea-f89d-4fab-a63d-7e88639e58f6
TargetOriginId: MyALB
ViewerProtocolPolicy: allow-all
Enabled: True
HttpVersion: http1.1
Origins:
- CustomOriginConfig:
HTTPPort: 80
OriginProtocolPolicy: http-only
DomainName: !GetAtt MyALB.DNSName
Id: MyALB
OriginCustomHeaders:
- HeaderName: Custom-Header
HeaderValue: !Ref MyCustomHeaderString
PriceClass: PriceClass_200
# -------------- VPC --------------
MyVPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: 10.0.0.0/16
InstanceTenancy: default
EnableDnsSupport: true
EnableDnsHostnames: true
Tags:
- Key: Name
Value: !Sub ${AWS::StackName}-MyVPC
#-------------- インターネットゲートウェイ --------------
# IGW
MyIGW:
Type: AWS::EC2::InternetGateway
Properties:
Tags:
- Key: Name
Value: !Sub ${AWS::StackName}-MyIGW
# アタッチメント
MyIGWAttach:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
InternetGatewayId: !Ref MyIGW
VpcId: !Ref MyVPC
# -------------- VPCエンドポイント --------------
MyVPCESSM:
Type: AWS::EC2::VPCEndpoint
Properties:
PrivateDnsEnabled: true
SecurityGroupIds:
- !Ref MyVPCESG
ServiceName: !Sub com.amazonaws.${AWS::Region}.ssm
SubnetIds:
- !Ref MyPriSN1
- !Ref MyPriSN2
VpcEndpointType: Interface
VpcId: !Ref MyVPC
MyVPCESSMMessages:
Type: AWS::EC2::VPCEndpoint
Properties:
PrivateDnsEnabled: true
SecurityGroupIds:
- !Ref MyVPCESG
ServiceName: !Sub com.amazonaws.${AWS::Region}.ssmmessages
SubnetIds:
- !Ref MyPriSN1
- !Ref MyPriSN2
VpcEndpointType: Interface
VpcId: !Ref MyVPC
MyVPCEEC2Messages:
Type: AWS::EC2::VPCEndpoint
Properties:
PrivateDnsEnabled: true
SecurityGroupIds:
- !Ref MyVPCESG
ServiceName: !Sub com.amazonaws.${AWS::Region}.ec2messages
SubnetIds:
- !Ref MyPriSN1
- !Ref MyPriSN2
VpcEndpointType: Interface
VpcId: !Ref MyVPC
MyVPCES3:
Type: AWS::EC2::VPCEndpoint
Properties:
RouteTableIds:
- !Ref MyPriRT1
- !Ref MyPriRT2
ServiceName: !Sub com.amazonaws.${AWS::Region}.s3
VpcEndpointType: Gateway
VpcId: !Ref MyVPC
# -------------- EIP --------------
MyNATGW1EIP:
Type: AWS::EC2::EIP
Properties:
Domain: vpc
MyNATGW2EIP:
Type: AWS::EC2::EIP
Properties:
Domain: vpc
# -------------- サブネット --------------
# パブリック
MyPubSN1:
Type: AWS::EC2::Subnet
Properties:
CidrBlock: 10.0.0.0/24
VpcId: !Ref MyVPC
AvailabilityZone: !Select [ 0, !GetAZs ]
MapPublicIpOnLaunch: true
Tags:
- Key: Name
Value: !Sub ${AWS::StackName}-MyPubSN1
MyPubSN2:
Type: AWS::EC2::Subnet
Properties:
CidrBlock: 10.0.2.0/24
VpcId: !Ref MyVPC
AvailabilityZone: !Select [ 1, !GetAZs ]
MapPublicIpOnLaunch: true
Tags:
- Key: Name
Value: !Sub ${AWS::StackName}-MyPubSN2
# プライベート
MyPriSN1:
Type: AWS::EC2::Subnet
Properties:
CidrBlock: 10.0.1.0/24
VpcId: !Ref MyVPC
AvailabilityZone: !Select [ 0, !GetAZs ]
Tags:
- Key: Name
Value: !Sub ${AWS::StackName}-MyPriSN1
MyPriSN2:
Type: AWS::EC2::Subnet
Properties:
CidrBlock: 10.0.3.0/24
VpcId: !Ref MyVPC
AvailabilityZone: !Select [ 1, !GetAZs ]
Tags:
- Key: Name
Value: !Sub ${AWS::StackName}-MyPriSN2
# -------------- ルートテーブル --------------
# ルートテーブル
MyPubRT:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref MyVPC
Tags:
- Key: Name
Value: !Sub ${AWS::StackName}-MyPubRT
MyPriRT1:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref MyVPC
Tags:
- Key: Name
Value: !Sub ${AWS::StackName}-MyPriRT1
MyPriRT2:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref MyVPC
Tags:
- Key: Name
Value: !Sub ${AWS::StackName}-MyPriRT2
# ルート
MyPubRoute:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref MyPubRT
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref MyIGW
MyPriRoute1:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref MyPriRT1
DestinationCidrBlock: 0.0.0.0/0
NatGatewayId: !Ref MyNATGW1
MyPriRoute2:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref MyPriRT2
DestinationCidrBlock: 0.0.0.0/0
NatGatewayId: !Ref MyNATGW2
# アソシエーション
MyPubSN1Assoc:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref MyPubSN1
RouteTableId: !Ref MyPubRT
MyPubSN2Assoc:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref MyPubSN2
RouteTableId: !Ref MyPubRT
MyPrivateSubne1Assoc:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref MyPriSN1
RouteTableId: !Ref MyPriRT1
MyPriSN2Assoc:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref MyPriSN2
RouteTableId: !Ref MyPriRT2
# -------------- NATゲートウェイ --------------
MyNATGW1:
Type: AWS::EC2::NatGateway
Properties:
AllocationId: !GetAtt MyNATGW1EIP.AllocationId
SubnetId: !Ref MyPubSN1
Tags:
- Key: Name
Value: !Sub ${AWS::StackName}-MyNATGW1
MyNATGW2:
Type: AWS::EC2::NatGateway
Properties:
AllocationId: !GetAtt MyNATGW2EIP.AllocationId
SubnetId: !Ref MyPubSN2
Tags:
- Key: Name
Value: !Sub ${AWS::StackName}-MyNATGW2
# -------------- ALB --------------
# ALB
MyALB:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
Properties:
Name: !Sub ${AWS::StackName}-MyALB
Scheme: internet-facing
Type: application
LoadBalancerAttributes:
- Key: deletion_protection.enabled
Value: false
- Key: idle_timeout.timeout_seconds
Value: 4000
SecurityGroups:
- !Ref MyALBSG
Subnets:
- !Ref MyPubSN1
- !Ref MyPubSN2
# リスナー
MyALBListener:
Type: AWS::ElasticLoadBalancingV2::Listener
Properties:
Port: 80
Protocol: HTTP
DefaultActions:
- FixedResponseConfig:
ContentType: text/plain
MessageBody: Access denied
StatusCode: 403
Type: fixed-response
LoadBalancerArn: !Ref MyALB
# リスナールール
MyALBListenerRule:
Type: AWS::ElasticLoadBalancingV2::ListenerRule
Properties:
Actions:
- TargetGroupArn: !Ref MyTG
Type: forward
Conditions:
- Field: http-header
HttpHeaderConfig:
HttpHeaderName: Custom-Header
Values:
- !Ref MyCustomHeaderString
ListenerArn: !Ref MyALBListener
Priority: 1
# ターゲットグループ
MyTG:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Properties:
VpcId: !Ref MyVPC
Name: !Sub ${AWS::StackName}-MyTG
Protocol: HTTP
ProtocolVersion: HTTP1
Port: 80
HealthCheckEnabled: true
HealthCheckProtocol: HTTP
HealthCheckPath: /
HealthCheckPort: 80
HealthyThresholdCount: 5
UnhealthyThresholdCount: 2
HealthCheckTimeoutSeconds: 5
HealthCheckIntervalSeconds: 30
IpAddressType: ipv4
Matcher:
HttpCode: 200
TargetType: instance
Targets:
- Id: !Ref MyEC2no1
Port: 80
- Id: !Ref MyEC2no2
Port: 80
Tags:
- Key: Name
Value: !Sub ${AWS::StackName}-MyTG
# -------------- EC2インスタンス --------------
MyEC2no1:
Type: AWS::EC2::Instance
Properties:
ImageId: ami-078296f82eb463377
InstanceType: t2.micro
IamInstanceProfile: !Ref MyInstanceProfile
NetworkInterfaces:
- AssociatePublicIpAddress: false
DeviceIndex: 0
SubnetId: !Ref MyPriSN1
GroupSet:
- !Ref MyEC2SG
UserData: !Base64 |
#!/bin/bash
sudo yum -y update
sudo yum -y upgrade
sudo yum -y install httpd
sudo systemctl start httpd
sudo systemctl enable httpd
sudo echo "hello! This is MyEC2no1!" > /var/www/html/index.html
Tags:
- Key: Name
Value: !Sub ${AWS::StackName}-MyEC2no1
MyEC2no2:
Type: AWS::EC2::Instance
Properties:
ImageId: ami-078296f82eb463377
InstanceType: t2.micro
IamInstanceProfile: !Ref MyInstanceProfile
NetworkInterfaces:
- AssociatePublicIpAddress: false
DeviceIndex: 0
SubnetId: !Ref MyPriSN2
GroupSet:
- !Ref MyEC2SG
UserData: !Base64 |
#!/bin/bash
sudo yum -y update
sudo yum -y upgrade
sudo yum -y install httpd
sudo systemctl start httpd
sudo systemctl enable httpd
sudo echo "hello! This is MyEC2no2!" > /var/www/html/index.html
Tags:
- Key: Name
Value: !Sub ${AWS::StackName}-MyEC2no2
# -------------- セキュリティグループ --------------
MyVPCESG:
Type: AWS::EC2::SecurityGroup
Properties:
VpcId: !Ref MyVPC
GroupDescription: for VPCE for Session Manager.
SecurityGroupIngress:
- SourceSecurityGroupId: !Ref MyEC2SG
IpProtocol: tcp
FromPort: 443
ToPort: 443
Tags:
- Key: Name
Value: !Sub ${AWS::StackName}-MyVPCESG
MyALBSG:
Type: AWS::EC2::SecurityGroup
Properties:
VpcId: !Ref MyVPC
GroupName: security group for ALB
GroupDescription: Allow HTTP access from Internet Only for your Global IP.
Tags:
- Key: Name
Value: !Sub ${AWS::StackName}-MyALBSG
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: 0.0.0.0/0
Description: Allow inbound HTTP access from all IPv4 addresses.
MyALBEgress:
Type: AWS::EC2::SecurityGroupEgress
Properties:
Description: Allow all protocol for MyEC2SG.
DestinationSecurityGroupId: !GetAtt MyEC2SG.GroupId
GroupId: !GetAtt MyALBSG.GroupId
IpProtocol: -1
MyEC2SG:
Type: AWS::EC2::SecurityGroup
Properties:
VpcId: !Ref MyVPC
GroupName: security group for EC2
GroupDescription: Allow HTTP access from MyALB, and Allow all protocol for all IPv4 addresses.
Tags:
- Key: Name
Value: !Sub ${AWS::StackName}-MyEC2SG
MyEC2Ingress:
Type: AWS::EC2::SecurityGroupIngress
Properties:
Description: Allow HTTP access from MyALB
FromPort: 80
ToPort: 80
IpProtocol: tcp
SourceSecurityGroupId: !GetAtt MyALBSG.GroupId
GroupId: !GetAtt MyEC2SG.GroupId
# ============== アウトプット ==============
Outputs:
MyCloudFrontDomain:
Value: !GetAtt MyCloudFront.DomainName
Export:
Name: !Sub ${AWS::StackName}-MyCloudFrontDomain
MyALBDomain:
Value: !GetAtt MyALB.DNSName
Export:
Name: !Sub ${AWS::StackName}-MyALBDNSName
出力タブにALBとDistributionのDNSがあります
両方クリックして、ALB× CloudFront○ である事を確認してみてください。
最後に
今日はあらためて自分以外の方が作られたテンプレートを読むのはとても勉強になるなぁと思いました。
この項目はなんの為に設定してるんだろうとか、どういう意図でこうしたんだろうと、作成された方の思惑を想像するのが好きです。
CloudFrontのPriceClassやCachePolicyなど思いがけず付随する知識に触れられたり、カスタムヘッダーにも馴染みがなかったのですが、可能ならパラメーターストアとかにStringで納めて呼び出したり出来ないかなとか、色々考えるのが面白かったです。
次回はCloudFrontにACM、Route53を紐づけたいと思っています。
クロスリージョンや循環参照など諸々問題がありそうですが楽しみです。
有難う御座いました!
Discussion