ITと創作でねこを飼いたい

プログラミングやITで勉強したこと、疑問をまとめます。

nginxのデフォルト画面を表示するCloudFormationのテンプレートファイル(yml)

今の現場でAWSを触っている& AWS 認定ソリューションアーキテクト–アソシエイトの資格も取得した!
けれどゼロからVPCとかサブネットとかEC2構築してって言われたら、まだ戸惑いそう……

てことで、自分のスキルの確立のために、
AWSの勉強の復習として、
CloudFormationのテンプレートファイルを作成することにしました。
(完全に備忘録です)

CloudFormationとは

AWS CloudFormation は Amazon Web Services リソースのモデル化およびセットアップに役立つサービスです。リソース管理に割く時間を減らし、AWS で実行するアプリケーションにさらに注力できるようになります。使用するすべての AWS リソース (Amazon EC2 インスタンスAmazon RDS DB インスタンスなど) を記述するテンプレートを作成すれば、AWS CloudFormation がお客様に代わってこれらのリソースのプロビジョニングや設定を受け持ちます。

つまりは、
テンプレートさえあれば、AWSのリソースのプロビジョニングや設定完成できるよーっていう話。

構成

VPC
Subnet : Private×3, Public×3(リージョンごとに一つずつ作成、備忘録として作成したので何もリソースを配置していない場所もある)
internet gateway
NAT gateway ALB
EC2
→Maintenance用にPublic Subnetに1台
→WEBサーバをPrivate Subnetに2台(リージョンは別)

テンプレート本体

私はコメント書けるので(現場でもこちらだったので)ymlで書いてます。

Github(今後も追加していきたいところ)

AWSTemplateFormatVersion: "2010-09-09"
Description: Memorandum of setting up AWS environment

# EC2のページでKeyは作成しておくこと
Parameters:
  WebKeyName:
    Description: The EC2 Key Pair to allow SSH access to the instance
    Type: AWS::EC2::KeyPair::KeyName

  ManagedKeyName:
    Description: The EC2 Key Pair to allow SSH access to the instance
    Type: AWS::EC2::KeyPair::KeyName

  SSHAccessSourceIP:
    Type: String
#
# NetworkAclはデフォルトにするものと仮定
#
Resources:
# ------------------------------------------------------------#
#  VPC
# ------------------------------------------------------------#
    MyVPC:
        Type: AWS::EC2::VPC
        Properties:
            CidrBlock: 10.0.0.0/16
            Tags:
            - Key: Name
              Value: MyVPC

    # InternetGateway Create
    InternetGateway:
        Type: AWS::EC2::InternetGateway
        Properties:
            Tags:
            - Key: Name
              Value: test-igw

    # IGW Attach
    InternetGatewayAttachment:
        Type: AWS::EC2::VPCGatewayAttachment
        Properties:
            InternetGatewayId: !Ref InternetGateway
            VpcId: !Ref MyVPC

# ------------------------------------------------------------#
#  Subnet
# ------------------------------------------------------------#
    # Public Subnet
    # Subnet Public1a
    PublicSubnet1a:
        Type: AWS::EC2::Subnet
        Properties:
            AvailabilityZone: ap-northeast-1a
            CidrBlock: 10.0.110.0/24
            VpcId: !Ref MyVPC
            Tags:
              - Key: Name
                Value: test-public-subnet-1a
    # Subnet Public1c
    PublicSubnet1c:
        Type: AWS::EC2::Subnet
        Properties:
            AvailabilityZone: ap-northeast-1c
            CidrBlock: 10.0.120.0/24
            VpcId: !Ref MyVPC
            Tags:
            - Key: Name
              Value: test-public-subnet-1c
    # Subnet Public1d
    PublicSubnet1d:
        Type: AWS::EC2::Subnet
        Properties:
            AvailabilityZone: ap-northeast-1d
            CidrBlock: 10.0.130.0/24
            VpcId: !Ref MyVPC
            Tags:
            - Key: Name
              Value: test-public-subnet-1d

    # Private Subnet
    # Subnet Private1a
    PrivateSubnet1a:
        Type: AWS::EC2::Subnet
        Properties:
            AvailabilityZone: ap-northeast-1a
            CidrBlock: 10.0.10.0/24
            VpcId: !Ref MyVPC
            Tags:
            - Key: Name
              Value: test-private-subnet-1a
    # Subnet Private1c
    PrivateSubnet1c:
        Type: AWS::EC2::Subnet
        Properties:
            AvailabilityZone: ap-northeast-1c
            CidrBlock: 10.0.20.0/24
            VpcId: !Ref MyVPC
            Tags:
            - Key: Name
              Value: test-private-subnet-1c
    # Subnet Private1d
    PrivateSubnet1d:
        Type: AWS::EC2::Subnet
        Properties:
            AvailabilityZone: ap-northeast-1d
            CidrBlock: 10.0.30.0/24
            VpcId: !Ref MyVPC
            Tags:
            - Key: Name
              Value: test-private-subnet-1d

# ------------------------------------------------------------#
# NAT Gateway AZ:A
# ------------------------------------------------------------#
    # ELPIP for NAT Gateway
    NATGatewayAEIP:
      Type: AWS::EC2::EIP
      Properties:
        Domain: vpc
    # NAT Gateway
    NATGatewayA:
      Type: AWS::EC2::NatGateway
      Properties:
        AllocationId: !GetAtt NATGatewayAEIP.AllocationId
        # memo : Only one setting
        SubnetId: !Ref PublicSubnet1a

# ------------------------------------------------------------#
#  RouteTable
# ------------------------------------------------------------#
    # Public RouteTable1a
    PublicRouteTable1a:
        Type: AWS::EC2::RouteTable
        Properties:
          VpcId: !Ref MyVPC
          Tags:
            - Key: Name
              Value: test-public-route-1a
    
    # Public RouteTable1c
    PublicRouteTable1c:
      Type: AWS::EC2::RouteTable
      Properties:
        VpcId: !Ref MyVPC
        Tags:
          - Key: Name
            Value: test-public-route-1c

    # Public RouteTable1c
    PublicRouteTable1d:
        Type: AWS::EC2::RouteTable
        Properties:
          VpcId: !Ref MyVPC
          Tags:
            - Key: Name
              Value: test-public-route-1d
            
    # Private RouteTable1a
    PrivateRouteTable1a:
      Type: AWS::EC2::RouteTable
      Properties:
        VpcId: !Ref MyVPC
        Tags:
          - Key: Name
            Value: test-private-route-1a
    
    # Private RouteTable1c
    PrivateRouteTable1c:
      Type: AWS::EC2::RouteTable
      Properties:
        VpcId: !Ref MyVPC
        Tags:
          - Key: Name
            Value: test-private-route-1c

    # Private RouteTable1d
    PrivateRouteTable1d:
        Type: AWS::EC2::RouteTable
        Properties:
          VpcId: !Ref MyVPC
          Tags:
            - Key: Name
              Value: test-private-route-1d

# ------------------------------------------------------------#
# Routing
# ------------------------------------------------------------#
    # Public
    # PublicRoute1a
    PublicRoute1a:
        Type: AWS::EC2::Route
        Properties:
          RouteTableId: !Ref PublicRouteTable1a
          DestinationCidrBlock: 0.0.0.0/0
          GatewayId: !Ref InternetGateway
    
    # PublicRoute1c
    PublicRoute1c:
      Type: AWS::EC2::Route
      Properties:
        RouteTableId: !Ref PublicRouteTable1c
        DestinationCidrBlock: 0.0.0.0/0
        GatewayId: !Ref InternetGateway

    # PublicRoute1d
    PublicRoute1d:
        Type: AWS::EC2::Route
        Properties:
          RouteTableId: !Ref PublicRouteTable1d
          DestinationCidrBlock: 0.0.0.0/0
          GatewayId: !Ref InternetGateway

    # Private
    # PrivateRoute1a
    PrivateRoute1a:
      Type: AWS::EC2::Route
      Properties:
        RouteTableId: !Ref PrivateRouteTable1a
        DestinationCidrBlock: 0.0.0.0/0
        NatGatewayId: !Ref NATGatewayA

    # PrivateRoute1c
    PrivateRoute1c:
      Type: AWS::EC2::Route
      Properties:
        RouteTableId: !Ref PrivateRouteTable1c
        DestinationCidrBlock: 0.0.0.0/0
        NatGatewayId: !Ref NATGatewayA

    # PrivateRoute1c
    PrivateRoute1d:
      Type: AWS::EC2::Route
      Properties:
        RouteTableId: !Ref PrivateRouteTable1d
        DestinationCidrBlock: 0.0.0.0/0
        NatGatewayId: !Ref NATGatewayA

# ------------------------------------------------------------#
# RouteTable Associate
# ------------------------------------------------------------#
    # PublicRouteTable Associate Subnet1a
    PublicSubnet1aRouteTableAssociation:
        Type: AWS::EC2::SubnetRouteTableAssociation
        Properties:
          SubnetId: !Ref PublicSubnet1a
          RouteTableId: !Ref PublicRouteTable1a
    
    # PublicRouteTable Associate Subnet1c
    PublicSubnet1cRouteTableAssociation:
      Type: AWS::EC2::SubnetRouteTableAssociation
      Properties:
        SubnetId: !Ref PublicSubnet1c
        RouteTableId: !Ref PublicRouteTable1c

    # PublicRouteTable Associate Subnet1d
    PublicSubnet1dRouteTableAssociation:
      Type: AWS::EC2::SubnetRouteTableAssociation
      Properties:
        SubnetId: !Ref PublicSubnet1d
        RouteTableId: !Ref PublicRouteTable1d

    # PrivateRouteTable Associate Subnet1a
    PrivateSubnet1aRouteTableAssociation:
      Type: AWS::EC2::SubnetRouteTableAssociation
      Properties:
        SubnetId: !Ref PrivateSubnet1a
        RouteTableId: !Ref PrivateRouteTable1a
    
    # PrivateRouteTable Associate Subnet1c
    PrivateSubnet1cRouteTableAssociation:
      Type: AWS::EC2::SubnetRouteTableAssociation
      Properties:
        SubnetId: !Ref PrivateSubnet1c
        RouteTableId: !Ref PrivateRouteTable1c

    # PrivateRouteTable Associate Subnet1d
    PrivateSubnet1dRouteTableAssociation:
        Type: AWS::EC2::SubnetRouteTableAssociation
        Properties:
          SubnetId: !Ref PrivateSubnet1d
          RouteTableId: !Ref PrivateRouteTable1d
# ------------------------------------------------------------#
# SecurityGroup
# ------------------------------------------------------------#

    # Managed
    ManagedSecurityGroup:
      Type: AWS::EC2::SecurityGroup
      Properties:
        GroupDescription: Managed EC2
        GroupName: Managed SecurityGroup
        VpcId: !Ref MyVPC
        Tags:
          - Key: Name
            Value: managed-sg
        # Rule
        SecurityGroupIngress:
          - IpProtocol: tcp
            FromPort: 22
            ToPort: 22
            CidrIp: !Ref SSHAccessSourceIP

    ManagedWebSecurityGroup:
      Type: AWS::EC2::SecurityGroup
      Properties:
        GroupDescription: Managed Web EC2
        GroupName: Managed Web SecurityGroup
        VpcId: !Ref MyVPC
        Tags:
          - Key: Name
            Value: managed-web-sg
        # Rule
        SecurityGroupIngress:
          - IpProtocol: tcp
            FromPort: 22
            ToPort: 22
            CidrIp: 10.0.110.0/24

    WebSecurityGroup:
      Type: AWS::EC2::SecurityGroup
      Properties:
        GroupDescription: web and ssh
        GroupName: web-sg
        VpcId: !Ref MyVPC
        Tags:
          - Key: Name
            Value: web-sg
    
    # Rule
    WebSecurityGroupIngress:
      Type: AWS::EC2::SecurityGroupIngress
      Properties:
        IpProtocol: tcp
        FromPort: 80
        ToPort: 80
        SourceSecurityGroupId: !GetAtt ALBSecurityGroup.GroupId
        GroupId: !GetAtt WebSecurityGroup.GroupId

    # ALB
    ALBSecurityGroup:
      Type: AWS::EC2::SecurityGroup
      Properties:
        GroupDescription: ALB
        GroupName: ALB SecurityGroup
        VpcId: !Ref MyVPC
        Tags:
          - Key: Name
            Value: alb-sg
        # Rule
        SecurityGroupIngress:
          - IpProtocol: tcp
            FromPort: 80
            ToPort: 80
            CidrIp: 0.0.0.0/0
          - IpProtocol: tcp
            FromPort: 443
            ToPort: 443
            CidrIp: 0.0.0.0/0
        SecurityGroupEgress:
          - IpProtocol: tcp
            FromPort: 80
            ToPort: 80
            DestinationSecurityGroupId: !Ref WebSecurityGroup

# ------------------------------------------------------------#
# EC2
# ------------------------------------------------------------#
    # Maintenance
    ManagedInstanceA:
      Type: AWS::EC2::Instance
      Properties:
        InstanceType: t2.micro
        ImageId: ami-0a1c2ec61571737db
        SecurityGroupIds:
          - !Ref ManagedSecurityGroup
        SubnetId: !Ref PublicSubnet1a
        KeyName: !Ref ManagedKeyName
        Tags:
          - Key: Name
            Value: Maintenance

    ManagedInstanceEIP:
      Type: AWS::EC2::EIP
      Properties:
        Domain: vpc
        InstanceId: !Ref ManagedInstanceA

    # Web
    WebInstanceA:
      Type: AWS::EC2::Instance
      Properties:
        InstanceType: t2.micro
        ImageId: ami-0a1c2ec61571737db
        SecurityGroupIds:
          - !Ref WebSecurityGroup
          - !Ref ManagedWebSecurityGroup
        SubnetId: !Ref PrivateSubnet1a
        KeyName: !Ref WebKeyName
        Tags:
          - Key: Name
            Value: Web-A
        UserData: !Base64 |
          #!/bin/bash
          sudo yum -y update
          sudo amazon-linux-extras install -y nginx1.12
          sudo service nginx start
    WebInstanceC:
      Type: AWS::EC2::Instance
      Properties:
        InstanceType: t2.micro
        ImageId: ami-0a1c2ec61571737db
        SecurityGroupIds:
          - !Ref WebSecurityGroup
          - !Ref ManagedWebSecurityGroup
        SubnetId: !Ref PrivateSubnet1c
        KeyName: !Ref WebKeyName
        Tags:
          - Key: Name
            Value: Web-C
        UserData: !Base64 |
          #!/bin/bash
          sudo yum -y update
          sudo amazon-linux-extras install -y nginx1.12
          sudo service nginx start
# ------------------------------------------------------------#
#  Target Group
# ------------------------------------------------------------#

    TargetGroup:
      Type: AWS::ElasticLoadBalancingV2::TargetGroup
      Properties:
        VpcId: !Ref MyVPC
        Name: web-tg
        Protocol: HTTP
        Port: 80
        HealthCheckProtocol: HTTP
        HealthCheckPath: /
        HealthCheckPort: traffic-port
        HealthyThresholdCount: 2
        UnhealthyThresholdCount: 2
        HealthCheckTimeoutSeconds: 5
        HealthCheckIntervalSeconds: 10
        Matcher:
          HttpCode: 200
        Tags:
          - Key: Name
            Value: web-tg
        TargetGroupAttributes:
          - Key: "deregistration_delay.timeout_seconds"
            Value: 300
          - Key: "stickiness.enabled"
            Value: false
          - Key: "stickiness.type"
            Value: lb_cookie
          - Key: "stickiness.lb_cookie.duration_seconds"
            Value: 86400
        Targets:
          - Id: !Ref WebInstanceA
          - Id: !Ref WebInstanceC
            Port: 80

# ------------------------------------------------------------#
#  Internet ALB
# ------------------------------------------------------------#

    InternetALB:
      Type: AWS::ElasticLoadBalancingV2::LoadBalancer
      Properties:
        Name: WebALB
        Tags:
          - Key: Name
            Value: WebALB
        Scheme: internet-facing
        LoadBalancerAttributes:
          - Key: deletion_protection.enabled
            Value: false
          - Key: idle_timeout.timeout_seconds
            Value: 60
        SecurityGroups:
          - !Ref ALBSecurityGroup
        Subnets:
          - !Ref PublicSubnet1a
          - !Ref PublicSubnet1c

    ALBListener:
      Type: AWS::ElasticLoadBalancingV2::Listener
      Properties:
        DefaultActions:
          - TargetGroupArn: !Ref TargetGroup
            Type: forward
        LoadBalancerArn: !Ref InternetALB
        Port: 80
        Protocol: HTTP

少し補足

今回のテンプレートはParametersはあまり設定しない方針です。
コメントに書いているように、EC2にアクセスするためのKeyは先に作成してください。
SSHAccessSourceIPは、Maintenanceサーバにアクセスすることができる唯一のIPとして設定します。
VPC, SubnetのIP(CIDR)の設定は適宜変更してください。

ちょっと他でみないのは、WEBサーバとしてたてたEC2のUserDataの部分でしょうか。

        UserData: !Base64 |
          #!/bin/bash
          sudo yum -y update
          sudo amazon-linux-extras install -y nginx1.12
          sudo service nginx start

AWSだとnginxはyumではなく、amazon-linux-extrasnginxでインストールしなければなりませんでした。
ここでnginxをstartさせておくことで、(EC2にSSH接続しなくても)ALBで指定されたDNSにアクセスしてnginxのデフォルト画面が表示されます。

おわりに

NAT gatewayが接続されていないPrivate Subnetだとyumが動かなかったり、
Private SubnetにWEBサーバおくにはALBが必要になったり……テンプレートファイル書くだけでかなりの復習になりました!!
今度は無料で動かせる範囲のLambdaの構築とかしたいです。

以上です!