【AWS】用語を整理しながら学ぶ AWS - part5 Cloud Formation - 4

# はじめに

この記事では AWS Cloud Tech を通して Cloud Formation を学習して実践していく記事です。
主な内容としては実践したときのメモを中心に書きます。（忘れやすいことなど）
誤りなどがあれば書き直していく予定です。

https://aws-cloud-tech.com/

## 前回までのおさらい

前回は VPC を構築してプライベートサブネットとパブリックサブネットに
ネットワークを切るところまでやりました。

[【AWS】用語を整理しながら学ぶ AWS - part5 Cloud Formation - 3](https://qiita.com/ymd65536/items/b75dcdbbf39c0f592a5c)

## 今回やること

前回構築した VPC 上に EC2 を建てる。
今回はキーペアを利用することとし、AMI の ID はハードコーディングする。

EC2 構築後、コラム的な話題として
いかにして認証情報をコードに載せないかについて調べた内容を共有します。

ちなみに今回は EC2 を建てるだけでかなりつまずいたのでその時のことも共有します。
※サブネット用のコードや VPC 用のコードを書きなおすハメになった。

| ファイル名          | 用途                               |
| :------------------ | :--------------------------------- |
| vpc_network.yml     | VPC の構成を管理                   |
| public_network.yml  | パブリックサブネットの構成を管理   |
| private_network.yml | プライベートサブネットの構成を管理 |
| public_ec2.yml      | パブリックサブネット内の EC2       |
| private_ec2.yml     | プライベートサブネット内の EC2     |

ということで
まず、VPC ネットワークを定義する。

```yml:vpc_network.yml
AWSTemplateFormatVersion: 2010-09-09
Resources:
  MainVpc:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: 10.0.0.0/21
      EnableDnsHostnames: true
      EnableDnsSupport: true
      Tags:
        - Key: Name
          Value: MainVpcfromCF
  IGW:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags:
        - Key: igw
          Value: igw-cf
  AttachGateway:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      VpcId: !Ref MainVpc
      InternetGatewayId: !Ref IGW

Outputs:
  MainVpc:
    Value: !Ref MainVpc
    Export:
      Name: MainVpcId
  MainIgw:
    Value: !Ref IGW
    Export:
      Name: MainIGWId


```

パブリックサブネットを定義する。
コンソールでパブリックサブネットを構築する手順を思い出していただけると
手順の中に

インターネットゲートウェイの作成
ルートテーブルの作成
ルートテーブルをサブネットにアタッチ(サブネットに関連付け)
ルートテーブルの設定

上記のような手順があったかと思います。

```yml:public_subnet.yml
AWSTemplateFormatVersion: 2010-09-09
Resources:
  subnetName:
    Type: AWS::EC2::Subnet
    Properties:
      AvailabilityZone: "ap-northeast-1a"
      VpcId: !ImportValue MainVpcId
      CidrBlock: 10.0.2.0/24
      Tags:
        - Key: Name
          Value: PublicSubnet
  PubRT:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !ImportValue MainVpcId
      Tags:
        - Key: PublicRoute
          Value: PublicRouteCf
  PubToInternet:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref PubRT
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !ImportValue MainIGWId
  routeTableAssocName:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref subnetName
      RouteTableId: !Ref PubRT
  secGroupName:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupName: PublicSecGrp
      GroupDescription: PublicSecGrp
      VpcId: !ImportValue MainVpcId
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 22
          ToPort: 22
          CidrIp: 0.0.0.0/0
      Tags:
        - Key: Name
          Value: MainVpc

Outputs:
  PublicSubnet:
    Value: !Ref subnetName
    Export:
      Name: PublicSubnet
  PublicSecGrp:
    Value: !GetAtt secGroupName.GroupId
    Export:
      Name: PublicSecGrp


```

続いてプライベートサブネットを定義する。
ポイントとしては
パブリックサブネットのIPアドレスからのみ
SSH接続を許可すること
また、インターネットゲートウェイはアタッチしないこと

※もしかすると、パブリック向けのルーティング書いてないっぽいので踏み台 SSH つながらないかも・・・

```yml:private_subnet.yml
AWSTemplateFormatVersion: 2010-09-09
Resources:
  subnetName:
    Type: AWS::EC2::Subnet
    Properties:
      AvailabilityZone: "ap-northeast-1a"
      VpcId: !ImportValue MainVpcId
      CidrBlock: 10.0.1.0/24
      Tags:
        - Key: Name
          Value: PrivateSubnet
  secGroupName:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupName: PrivateSecGrp
      GroupDescription: PrivateSecGrp
      VpcId: !ImportValue MainVpcId
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 22
          ToPort: 22
          CidrIp: 10.0.2.0/24
      Tags:
        - Key: Name
          Value: MainVpc

Outputs:
  PrivateSubnet:
    Value: !Ref subnetName
    Export:
      Name: PrivateSubnet

```

パブリックサブネットに EC2 を配置する。

```yml:public_ec2.yml

AWSTemplateFormatVersion: 2010-09-09
Resources:
  myEC2Instance:
    Type: AWS::EC2::Instance
    Properties:
      KeyName: PublicEc2Key
      ImageId: ami-0992fc94ca0f1415a
      InstanceType: t2.micro
      Monitoring: false
      NetworkInterfaces:
        - AssociatePublicIpAddress: true
          DeviceIndex: "0"
          GroupSet:
            - !ImportValue PublicSecGrp
          PrivateIpAddress: "10.0.2.10"
          SubnetId: !ImportValue PublicSubnet
      Tags:
        - Key: Name
          Value: PublicEC2

```

プライベートサブネットに EC2 を配置する。

```yml:private_ec2.yml

AWSTemplateFormatVersion: 2010-09-09
Resources:
  myEC2Instance:
    Type: AWS::EC2::Instance
    Properties:
      KeyName: PrivateEc2Key
      ImageId: ami-0992fc94ca0f1415a
      InstanceType: t2.micro
      Monitoring: false
      NetworkInterfaces:
        - AssociatePublicIpAddress: false
          DeviceIndex: "0"
          PrivateIpAddress: "10.0.1.15"
          SubnetId: !ImportValue PrivateSubnet
      Tags:
        - Key: Name
          Value: PrivateEC2

```

### EC2 を テンプレートで定義すると(前回)

前回の内容と今回の内容を比べてみると驚くべきことがわかった。

```yml:public_ec2.yml
AWSTemplateFormatVersion: 2010-09-09
Resources:
  myEC2Instance:
    Type: AWS::EC2::Instance
    Properties:
      KeyName: Mykeypair
      ImageId: ami-0992fc94ca0f1415a
      InstanceType: t2.micro
      Monitoring: false
      SecurityGroupIds:
        - !ImportValue PublicSecGrp
      SubnetId: !ImportValue PublicSubnet
      Tags:
        - Key: Name
          Value: CFec2

```

こんな感じになるかと思うのですが
実はこれには一つ良くない点があります。

ネットワークインターフェイスにパブリック IP が振られないことです。
パブリック IP を AWS に振ってもらうには Properties の NetworkInterfacesで
AssociatePublicIpAddress を trueにしてあげなければなりません。

また、NetworkInterfacesで
サブネットとセキュリティグループ、プライベートIPアドレスの指定を書く場合は
他のプロパティに同じ記述を残してはいけません。

NetworkInterfaces 以外にも
サブネットとセキュリティグループ、プライベートIPアドレスの指定を書いた場合は
以下のようなエラーが出てロールバックが走ります。

>Network interfaces and an instance-level subnet ID may not be specified on the
> same request (Service: AmazonEC2; Status Code: 400; Error Code: >
> InvalidParameterCombination; Request ID: c07729aa-c10f-4ce0-93a8-c800a92a3b32; Proxy: null)

### EC2 を テンプレートで定義すると(今回)

前述の内容を踏まえて書き直すと。。。

```yml:public_ec2.yml
AWSTemplateFormatVersion: 2010-09-09
Resources:
  myEC2Instance:
    Type: AWS::EC2::Instance
    Properties:
      KeyName: PublicEc2Key
      ImageId: ami-0992fc94ca0f1415a
      InstanceType: t2.micro
      Monitoring: false
      NetworkInterfaces:
        - AssociatePublicIpAddress: true
          DeviceIndex: "0"
          GroupSet:
            - !ImportValue PublicSecGrp
          PrivateIpAddress: "10.0.2.10"
          SubnetId: !ImportValue PublicSubnet
      Tags:
        - Key: Name
          Value: PublicEC2

```

見慣れないワードとして「DeviceIndex: "0"」があると思います。
これはイーサネットインターフェイスデバイスの番号を0とするという指定になります。

なぜ、0になるかと言えば
パブリックIPアドレスを割り当てることができるインターフェイスはeth0 つまり、0だからです。

## 余談

### EC2のアクセスに鍵を使わない場合

今回はペアキーを利用することでEC2を構築しましたが
実際にはEC2をたくさん構築しないといけないので
一々、鍵を保持していたら運用面で支障が出ます。

かといってテンプレートに認証情報を埋め込むわけにいきません。
※セキュリティ的にもAWSのベストプラクティスにも反するので

認証情報を管理しなければ、EC2へのアクセスはできないので
管理しないという選択肢はありません。

### じゃあどうすれば

AWS SSMもしくはAWS Secrets Managerを使いましょう。
また、AWS CLIを使って裏技っぽく構成に認証情報を含めないという方法もなくはないみたいですが
手動で実行しなければならない為、あまりオススメできません。

## まとめ

Cloud Formation を利用して構築したVPC ネットワークにEC2 を構築しました。

もしかすると、プライベートサブネット向けのルーティングが
設定されていないので今の段階では踏み台SSHができないかもしれません。

次回は構築したネットワークの検証をやります。

## おわり