CloudFormationの基礎+localからRDSに10分で接続する方法

#この記事の説明
cloudformationの定義の仕方を初めにまとめしたので共有いたします。
↓
その後にlocalからRDSに10分で接続する方法をcloudformationを使って共有します。

#CloudFormationの仕組み
Cloudformationの大きな区分は以下

- AWSTemplateFormatVersion
- Description
- Parameters
- Resources

AWSTemplateFormatVersionはテンプレのバージョン

Descriptionはこのファイルの説明

Resourcesでインスタンスやリソースの定義をする。
ResourcesではResourcesやParametersの変数を参照してインスタンスやリソースの定義をする。

ParametersではResourcesで参照される変数を定義。
ResourcesはParametersで定義した変数を参照することで、リソース作成時に、参照した項目に関しては入力や選択が可能になる

###Resourcesの書き方

```yml
name: リソースの名前
  type: そのリソースの定義したい物
  Properties: リソースの定義に必要な設定や値
```

具体例

```yml
---
    AWSTemplateFormatVersion: '2010-09-09'
    Description: 'Setup for VPC. This purpose is to enable to connect RDS from local'
    Resources:
      #VPC
      myVPC:
        Type: AWS::EC2::VPC
        Properties: 
          CidrBlock: 10.0.0.0/16
          EnableDnsSupport: 'true'
          EnableDnsHostnames: 'true'
          InstanceTenancy: dedicated
          Tags:
          - Key: Name
            Value: myVPC
```

###Parametersの書き方

```yml
name: パラメーターの名前
  type: 入力内容の種類
  Default: デフォルト入力値
  AllowedValues: 入力許可されている内容(選択肢)
  Description: 入力の内容の説明
```

具体例

```yml
---
    AWSTemplateFormatVersion: '2010-09-09'
    Description: 'Setup for EC2 and RDS. This purpose is to enable to connect RDS from local'
    Parameters:
      InstanceType: 
        Type: String
        Default: t2.micro
        AllowedValues: 
          - t2.micro
          - t2.nano
          - t2.nano
        Description: Enter t2.micro, t2.nano, or t2.nano. Default is t2.micro.
```

###その他補足
#####Ref
定義された変数を参照するときはRefを使用して参照する

```yml
---
    AWSTemplateFormatVersion: '2010-09-09'
    Description: 'Setup for VPC. This purpose is to enable to connect RDS from local'
    Resources:
      #VPC
      myVPC:
        Type: AWS::EC2::VPC
        Properties: 
          CidrBlock: 10.0.0.0/16
          EnableDnsSupport: 'true'
          EnableDnsHostnames: 'true'
          InstanceTenancy: dedicated
          Tags:
          - Key: Name
            Value: myVPC
      #RouteTable
      PublicRouteTable:
        Type: AWS::EC2::RouteTable
        Properties:
          VpcId:  
            Ref: myVPC
          Tags:
          - Key: Name
            Value: PublicRouteTable
```

#####Fn::GetAtt:
リソースのID(数字)を参照したいが、まだ作成が完了していないのでそのリソースのIDを把握できない場合に使用。参照したいリソース(未作成)のIDを参照することができる。

以下の場合まだEC2SecurityGroupの作成が完了されていないが、myEc2InstanceではEC2SecurityGroupのIDを知りたい模様。

```yml
    Resources:
      #ec2
      EC2SecurityGroup:
        Type: AWS::EC2::SecurityGroup
        Properties:
          #vpc_for_ec2_rds.ymlでできたvpc
          VpcId: vpc-xxx
          GroupDescription: Enable SSH access via port 22
          SecurityGroupIngress:
            - IpProtocol: tcp
              FromPort: '22'
              ToPort: '22'
              CidrIp:
                Ref: SSHLocation
      myEc2Instance: 
        Type: AWS::EC2::Instance
        Properties: 
          SecurityGroupIds:
            - Fn::GetAtt: [ EC2SecurityGroup, GroupId ]
```

参考：https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-getatt.html

#localからRDSに10分で接続する方法
![スクリーンショット 2020-12-31 13.50.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/510817/4bbdeb54-c4a4-9cbd-c5f6-daeb7495a943.png)

localからrdsにつなぐためにbassionEc2を介して接続したい人のためのVPCとEC2とRDSをcloudformationで作成するymlを定義しましたので共有いたします。
https://github.com/Tsuyoshi-Ishikawa/bassion_to_rds


### 作るリソース
#####VPC系
- VPC x 1
- publicSubnet x 1(EC2紐付け)
- privateSubnet x 2(rdsSubnetGroupに紐付け)
- publicRouteTable x 1(publicSubnet用)
- privateRouteTable x 2(privateSubnet用)
- internetGateWay x 1(publicSubnet用)

#####EC2系
- EC2SecurityGroup x 1(ec2用)
- EC2 x 1

#####RDS系
- rdsSubnetGroup x 1(RDS用)
- RDSSecurityGroup x 1(RDS用)
- RDS x 1

### 手順
vpc_for_ec2_rds.ymlでvpcスタック作成
↓
rdsSubnetG_for_ec2_rds.ymlでrdsSubnetGroupを作成
↓
ec2_rds.ymlでec2とrdsスタックを作成
↓
ec2にelasticIPを割り当て
↓
ec2に入ってrdsのmysql導入・接続確認、db作成
laravelの場合は以下を参考に
https://noumenon-th.net/programming/2020/04/10/ec2-rds-laravel/
↓
DB接続のためにバックエンドアプリの設定変更
laravelの場合は以下を参考に.envを修正
https://noumenon-th.net/programming/2020/04/10/ec2-rds-laravel/
↓
localからのport forwarding
https://dev.classmethod.jp/articles/rds-portforward/
Ec2のsecurityGがsshのためのport22しか開放していなくてもできる

####注意
localのdockerコンテナからつなぐ場合はコンテナにawsのキーペアをvolumeしないといけないのでその手間忘れない

```yml:vpc_for_ec2_rds.yml
---
    AWSTemplateFormatVersion: '2010-09-09'
    Description: 'Setup for VPC. This purpose is to enable to connect RDS from local'
    Resources:
      #VPC
      myVPC:
        Type: AWS::EC2::VPC
        Properties: 
          CidrBlock: 10.0.0.0/16
          EnableDnsSupport: 'true'
          EnableDnsHostnames: 'true'
          InstanceTenancy: dedicated
          Tags:
          - Key: Name
            Value: myVPC
      #RouteTable
      PublicRouteTable:
        Type: AWS::EC2::RouteTable
        Properties:
          VpcId:  
            Ref: myVPC
          Tags:
          - Key: Name
            Value: PublicRouteTable
      PrivateRouteTable1:
        Type: AWS::EC2::RouteTable
        Properties:
          VpcId:  
            Ref: myVPC
          Tags:
          - Key: Name
            Value: PrivateRouteTable1
      PrivateRouteTable2:
        Type: AWS::EC2::RouteTable
        Properties:
          VpcId:  
            Ref: myVPC
          Tags:
          - Key: Name
            Value: PrivateRouteTable2
      #Subnet
      PublicSubnet:
        Type: AWS::EC2::Subnet
        Properties:
          VpcId:
            Ref: myVPC
          CidrBlock: 10.0.1.0/24
          AvailabilityZone: "ap-northeast-1a"
          Tags:
          - Key: Name
            Value: PublicSubnet
      PrivateSubnet1:
        Type: AWS::EC2::Subnet
        Properties:
          VpcId:
            Ref: myVPC
          CidrBlock: 10.0.2.0/24
          AvailabilityZone: "ap-northeast-1a"
          Tags:
          - Key: Name
            Value: PrivateSubnet1
      PrivateSubnet2:
        Type: AWS::EC2::Subnet
        Properties:
          VpcId:
            Ref: myVPC
          CidrBlock: 10.0.4.0/24
          AvailabilityZone: "ap-northeast-1c"
          Tags:
          - Key: Name
            Value: PrivateSubnet2
      #SubnetRouteTableAssociation(subnetとrouteTableを紐付け、これだけではprivateのまま)
      PublicSubnetRouteTableAssociation:
        Type: AWS::EC2::SubnetRouteTableAssociation
        Properties: 
          SubnetId: 
            Ref: PublicSubnet
          RouteTableId: 
            Ref: PublicRouteTable
      PrivateSubnet1RouteTableAssociation:
        Type: AWS::EC2::SubnetRouteTableAssociation
        Properties: 
          SubnetId: 
            Ref: PrivateSubnet1
          RouteTableId: 
            Ref: PrivateRouteTable1
      PrivateSubnet2RouteTableAssociation:
        Type: AWS::EC2::SubnetRouteTableAssociation
        Properties: 
          SubnetId: 
            Ref: PrivateSubnet2
          RouteTableId: 
            Ref: PrivateRouteTable2
      #InternetGateway
      myInternetGateway:
        Type: AWS::EC2::InternetGateway
        Properties: 
          Tags: 
          - Key: Name
            Value: myInternetGateway
      #AttachGateway(VPCとInternetGatewayの紐付け)
      AttachGateway:
        Type: AWS::EC2::VPCGatewayAttachment
        Properties:
          VpcId:
            Ref: myVPC
          InternetGatewayId:
            Ref: myInternetGateway
      #AWS:EC2:Route(routeTableとInternetGatewayを紐付け、これをされたrouteTableはpublicになる)
      myRoute:
        Type: AWS::EC2::Route
        DependsOn: myInternetGateway
        Properties:
          RouteTableId:
            Ref: PublicRouteTable
          DestinationCidrBlock: 0.0.0.0/0
          GatewayId:
            Ref: myInternetGateway
```

```yml:rdsSubnetG_for_ec2_rds.yml
---
    AWSTemplateFormatVersion: '2010-09-09'
    Description: 'Setup for VPC. This purpose is to enable to connect RDS from local'
    Resources:
      #rdsのサブネットグループ
      RdsSubnetGroup:
        Type: AWS::RDS::DBSubnetGroup
        Properties: 
          DBSubnetGroupDescription: 'This is RdsSubnetGroup'
          DBSubnetGroupName: RdsSubnetGroup
          SubnetIds: 
            - subnet-aaa
            - subnet-bbb
            #前回のvpc_for_ec2_rds.ymlで作られたprivate_subnetのidをここに記述
            #2種のazに紐づいたsubnetを紐付け
          Tags: 
          - Key: Name
            Value: RdsSubnetGroup
```

```yml:ec2_rds.yml
---
    AWSTemplateFormatVersion: '2010-09-09'
    Description: 'Setup for EC2 and RDS. This purpose is to enable to connect RDS from local'
    Parameters:
      #ec2_parameter
      #AWS::EC2::Instanceでamiを選択して作成している場合はInstanceのtypeは設定できない
      # InstanceType: 
      #   Type: String
      #   Default: t2.micro
      #   AllowedValues: 
      #     - t2.micro
      #     - t2.nano
      #     - t2.nano
      #   Description: Enter t2.micro, t2.nano, or t2.nano. Default is t2.micro.
      KeyName: 
        Description: Amazon EC2 Key Pair
        Type: "AWS::EC2::KeyPair::KeyName"
      SSHLocation:
        Type: String
        MinLength: '9'
        MaxLength: '18'
        Default: 0.0.0.0/0
        AllowedPattern: "(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})/(\\d{1,2})"
        ConstraintDescription: must be a valid IP CIDR range of the form x.x.x.x/x.
        Description: " Enter the IP address range that is allowed to connect to EC2"
      #rds_parameter
      RDSInstanceIdentifier:
          Type: String
          Default: database-1
          Description: Enter RDS InstanceIdentifier to identify RDS.
      RDSInstanceType:
          Type: String
          Default: db.r5.large
          AllowedValues:
          - db.r5.large
          - db.r5.xlarge
          - db.r5.2xlarge
          - db.r5.4xlarge
          Description: Enter RDS InstanceType
      RDSDBPort:
          Default: '3306'
          Type: Number
          MinValue: '1150'
          MaxValue: '65535'
          Description: Enter TCP/IP port used to connect DB in RDS.
      RDSDBPwd:
          NoEcho: 'true'
          Type: String
          MinLength: '1'
          MaxLength: '41'
          AllowedPattern: "^[a-zA-Z0-9]*$"
          Description: Enter the database admin account password in RDS
    Resources:
      #ec2
      EC2SecurityGroup:
        Type: AWS::EC2::SecurityGroup
        Properties:
          #vpc_for_ec2_rds.ymlでできたvpc
          VpcId: vpc-xxx
          GroupDescription: Enable SSH access via port 22
          SecurityGroupIngress:
            - IpProtocol: tcp
              FromPort: '22'
              ToPort: '22'
              CidrIp:
                Ref: SSHLocation
      myEc2Instance: 
        Type: AWS::EC2::Instance
        Properties: 
          #自分が既に持っているec2_ami
          ImageId: ami-xxx
          #ここにはvpcのcfnで作られたpublic subnet idを割り当てる
          SubnetId: subnet-xxx
          KeyName: 
            Ref: KeyName
          #SecurityGroupIds:を設定しなければdefaultのSecurityGroupが割り当てられる
          SecurityGroupIds:
            - Fn::GetAtt: [ EC2SecurityGroup, GroupId ]
          #   #上記はGetattを使うと、そのリソースがまだ作られていなくても、そのリソースの名前を得たりすることができる
          #   #https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-getatt.html
      #rds
      RDSSecurityGroup:
        Type: AWS::EC2::SecurityGroup
        Properties:
          GroupDescription: SecurityGroup rds-grp
          #vpc_for_ec2_rds.ymlでできたvpc
          VpcId: vpc-xxx
          SecurityGroupIngress:
            - IpProtocol: tcp
              FromPort:
                Ref: RDSDBPort
              ToPort:
                Ref: RDSDBPort
              SourceSecurityGroupId:
                Ref: EC2SecurityGroup
      RDSInstance:
        Type: AWS::RDS::DBInstance
        Properties:
          Engine: mysql
          DBInstanceClass:
            Ref: RDSInstanceType
          AllocatedStorage: '20'
          StorageType: gp2
          DBInstanceIdentifier:
            Ref: RDSInstanceIdentifier
          MasterUsername: admin
          MasterUserPassword:
            Ref: RDSDBPwd
          #rdsのサブネットGを設定しないといけないため、そのためだけの構築工数が必要(rdsSubnetG_for_ec2_rds.yml)
          #ここにはそれでできたRDSSubnetGroupの名前を入れる
          DBSubnetGroupName: "xxx"
          PubliclyAccessible: false
          AvailabilityZone: "ap-northeast-1a"
          VPCSecurityGroups:
            - Ref: RDSSecurityGroup
          CopyTagsToSnapshot: true
          BackupRetentionPeriod: 7
          Tags:
            - Key: Application
              Value: string
        DeletionPolicy: Snapshot

```

# 作ってみた感想
- EC2やRDSを立てる前にネットワーク周りVPCの設定、定義が大切

- rds作成にはrdsSubnetGをsubnetIdを用いて作らなければいけない
vpc作成をした後にdsSubnetGを作成しないといけない。しかも設定する内容も多いので実践ではRDSをcfnで定義しないこともある模様

- インデントの仕方ひとつ違うだけでダメと言われるので注意必要

# cloudformation参考文献

- Ec2-rds
https://qiita.com/OPySPGcLYpJE0Tc/items/f0ae690d971e96006b38

- rdsのみ
https://qiita.com/okubot55/items/87d4bd7a3649992bc5f7
https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/quickref-rds.html

- ec2のみ
https://www.udemy.com/course/aws-associate/learn/lecture/13658638#overview

- Cfnの設定方法
https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/parameters-section-structure.html
各リソースの定義方法は以下を参考に行う
https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-subnet-route-table-assoc.html