【初心者向け】CloudFormationテンプレートファイルを作成して自動構築してみる(ハンズオン)

こんにちは。あらたです。最近AWS認定試験のために、CloudFormationについて学びました。

せっかくなので、AWS初心者目線で、CloudFormtionを使用して、AWSリソースをデブロイする方法をわかりやすく解説していきたいと思います。

あらた

なるべく、図を多く使って理解しやすいようにしていきますので安心してください!

目次

本記事でCloudFormationを使用してデプロイする構成

早速ですが、今回CloudFormationを使用してデプロイを目指す構成はこちらです。こちらのAWS Cloud内にあるサービス全てをCloudFormationを使用してデプロイしていきます。

ただサービスをデプロイできたことを確認するだけでは、面白みに欠けますよね。

そのため、下の構成図のように、CloudFormationを使用してデプロイした、踏み台サーバを経由して、プライベートサブネットにあるEC2インスタンスにログインすることを本記事の最終目標としました。

CloudFormationを使用してVPCを作成する設定

習うより慣れろということで、早速設定ファイルを作成してみましょう。まずは、VPCを作成してみます。

図にしなくてもわかると思いますが、この状態です笑。今回は、東京リージョンを使用していきますので、マネジメントコンソールは、ap-northeast-1を選択した状態にしてください。

後ほどリージョンがap-northeast-1以外だとエラーになってしまう設定が出てくるので、気をつけてくださいね。

早速ですが、VPCを作成するために必要なyaml設定はこれです↓

AWSTemplateFormatVersion: '2010-09-09'
#+++++++++++++++++++++++++++++++
# TestVPCという名前のVPCを作成する
#+++++++++++++++++++++++++++++++
Resources:
  TestVPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: 10.0.0.0/16
      Tags:
      - Key: Name
        Value: FirstVPC

最初の行のAWSTemplateFormatVersion: ‘2010-09-09’は、テンプレート機能を識別するフォーマットバーションのことです。おまじないだと思って必ず入れておいてください。

Resourceの中に作成するサービスを記述していきます。今回だったらVPCですね。もちろん今後、この中にVPC以外のサービスも記述していきます。

次に、Typeの部分。こちらは、何を作成するのか種類を指定する場所です。指定できるリソースタイプの種類は公式ドキュメントのリファレンスに詳しく書かれています。作成するサービスによって違いますので、公式ドキュメントを読んでみましょう。→公式ドキュメント

PropertiesはTypeで指定したサービスの詳細設定を記述する部分です。上記の場合は、「10.0.0.0/16のCIDRでVPCを作り、VPCのNameタグはFirstVPCにする」という内容が指定されています。

これだけでVPCが作成できてしまうんです。思ったより簡単ではないですか?僕はCloudFormationって難しい物でしょ?と遠ざけていましたが、これなら理解できますね。

CloudFormationを使ってVPCをデプロイしてみよう

それでは、VPCが作成することができるか、確認してみましょう。

まずは、サービスの横にある検索バーでCloudFormationと検索します。

スタックの作成を選択します。

テンプレートの指定で「テンプレートのアップロード」を選択し、先ほど作成したyamlファイルをアップロードします。次へを選択します。

スタックの詳細を設定で「スタック名」を記述します。今回は。CloudFormation-testと名付けました。

名前を入力したら「次へ」を選択します。

詳細オプション等は変更せず「次へ」を選択します。

次の画面もデフォルトのまま変更せずに「送信」を選択します。

「送信」後CloudFormationのスタックが作成されます。

しばらくしてから「CREATE_COMPLETE」と表示されたら、デプロイ成功です。

では、VPCが作成されているか実際に見てみましょう。右上にある検索欄にVPCと入力します。

VPCのダッシュボードからVPCを選択すると設定した名前のVPCが作成されていることが確認できると思います。

あらた

無事VPCは作成できましたか?続いてVPCの中にプライベートサブネットとパブリックサブネットをそれぞれ一つずつ作成していきます。

VPCの中にプライベートサブネットとパブリックサブネットを作成する設定を確認しよう

それでは早速サブネットを作成していきます。以下の内容を先ほど作成したyamlファイルに追加してください。

#+++++++++++++++++++++++++++++++
# Publicサブネットを作成する
#+++++++++++++++++++++++++++++++
  PublicSubnet:
    Type: AWS::EC2::Subnet
    Properties:
      CidrBlock: 10.0.1.0/24
      MapPublicIpOnLaunch: 'true'
      VpcId: !Ref TestVPC
      Tags:
      - Key: Name
        Value: TestVPC-PublicSubnet
#+++++++++++++++++++++++++++++++
# Privateサブネットを作成する
#+++++++++++++++++++++++++++++++
  PrivateSubnet:
    Type: "AWS::EC2::Subnet"
    Properties:
      CidrBlock: 10.0.2.0/24
      MapPublicIpOnLaunch: 'false'
      VpcId: !Ref TestVPC
      Tags:
      - Key: Name
        Value: TestVPC-PrivateSubnet

パブリックサブネットの作成

まずはパブリックサブネットの作成方法について解説します。

パブリックサブネットなのでMaPulicIp0nLaunchをTrueに設定します。この設定で起動時にバプリックIPアドレスを設定できます。

次にVpc Id:の部分に注目してみてください。!Ref Test VPCと記述されています。!Refとはいったい何でしょうか?

!Ref (組み込み関数)とは?

!Refを説明する前に、VPCIDについて簡単に解説します。

VPC IDとはVPCを作成した際に付与されるVPCの番号のことです。こちらはVPCごと固有の値になるので、この値によって特定のVPCを指定することができます。

では、本題の!Refについての解説です。

!Refを使用するとテンプレートファイル内で指定したリソースの値を参照することができます。

上の例では、VpcIdで使用しています。VpcIdは先ほども説明しましたが、VPCを作成するまでわかりません。そういった時に、!Refを使用することで、yamlファイル作成段階ではわからないIDを指定することができます。!Refの後には、最初に作成したVPCの名前を入れてあげます。

この!Refのことを組み込み関数と呼びます。CloudFormationには、他にも組み込み関数が多数用意されているので、確認してみてください。→組み込み関数リファレンス

ちなみにこちらのブログの記事が組み込み関数についてすごくわかりやすく解説しています↓僕も助けられました。

プライベートサブネットの作成

パブリックサブネットとの違いはMapulicIp0nLaunchがfalseとなっている部分だけです。これはプライベートサブネットなのでfalseになっているだけです。VpcIdは先ほどと同様に!Refを使用してTest VPCを指定あげています。

プライベートサブネットとパブリックサブネットをデプロイするCloudFormationを実行しよう

それでは、追加したyamlファイルを使ってプライベートサブネットとパブリックサブネットをデプロイしてみましょう。CloudFormationから先ほど作成したCloudFormation-testを選択します。

「更新」を選択します。

「既存のテンプレートを置き換える」を選択します。テンプレートファイルのアップロードから追加したファイルを選択し「次へ」を選択します。

スタックの詳細を設定から「次へ」を選択します。

そのままの設定で「次へ」を選択します。

「変更セットのプレイビュー」でPrivateSubnetとPrivateSubnetが追加されていることを確認し「送信」を選択します。

スタックが「UPDATE_COMPLETE」となっていることを確認してください。

サブネットが作成されているか確認してみましょう。VPC→サブネットから確認することができます。

「TestVPC-PublicSubnet」「TestVPC-PrivateSubnet」が作成されていたらサブネットの作成も完了しました。

インターネットゲートウェイ(IGW)を作成してVPCにアタッチする設定を確認しよう

続いてインターネットゲートウェイ(IGW)を作成してVPCにアタッチするコードを作成します。

#+++++++++++++++++++++++++++++++
# インターネットゲートウェイを作成する
#+++++++++++++++++++++++++++++++
  InternetGateway:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags:
      - Key: Name
        Value: FirstVPC-IGW

#+++++++++++++++++++++++++++++++
# インターネットゲートウェイをVPCにアタッチする
#+++++++++++++++++++++++++++++++
  AttachGateway:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      VpcId: !Ref TestVPC
      InternetGatewayId: !Ref InternetGateway

先ほど使用した!Refはここでも使用しています。この後も!Refは使用するのでここでマスターしてしまいましょう。

インターネットゲートウェイを作成して、作成したインターネットゲートウェイをVPCにアタッチしています。

  VpcId: !Ref TestVPC
  InternetGatewayId: !Ref InternetGateway

こちらの二行でアタッチするVPCIDとインターネットゲートウェイのIDを指定しています。

インターネットゲートウェイ(IGW)を作成してVPCにアタッチするCloudFormationを実行しよう

先ほどと同様の手順で、CloudFormationを更新してインターネットゲートウェイをデプロイして、作成したインターネットゲートウェイをVPCにアタッチしてみましょう。

CloudFormationの更新の仕方は忘れてませんよね?笑こちらの更新からポチぽち進めてファイルを新しいものに更新するだけです。

インターネットゲートウェイをアタッチすることができたか確認してみましょう。

VPC→インターネットゲートウェイを確認するとFirstVPC-IGWが作成されているはずです。

ルートテーブル関連を設定しよう

#+++++++++++++++++++++++++++++++
# カスタムルートテーブルを作成する
#+++++++++++++++++++++++++++++++
  PublicRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref TestVPC
      Tags:
      - Key: Name
        Value: TestVPC-PublicRoute

#+++++++++++++++++++++++++++++++
# ルートテーブルにデフォルトルートを追加する
#+++++++++++++++++++++++++++++++
  PublicRoute:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref PublicRouteTable
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref InternetGateway
      
#+++++++++++++++++++++++++++++++
# Publicサブネットをルートテーブルに関連付ける
#+++++++++++++++++++++++++++++++
  PublicSubnetRouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PublicSubnet
      RouteTableId: !Ref PublicRouteTable

カスタムルートテーブルを作成する

まずはカスタムルートテーブルを作成する設定です。ここで一つ疑問が生まれると思います。ルートテーブルはVPCを作成した際に一緒に作成されるはずでは?という疑問です。

確かにVPCを作成した際にルートテーブルは合わせて作成されます。

実際にVPCのルートテーブルを確認してみると、VPCを作成した時点でルートテーブルも作成されていることがわかります。VPCを作成時に作成されるルートテーブルは「メイン」→「はい」と表示されます。

この「メイン」→「はい」と表示されているルートテーブルを「メインルートテーブル」と言います。

「メイン」→「いいえ」と表示されているものは自身で作成したルートテーブルで「カスタムルートテーブル」と言います。

では、なぜメインルートテーブルが作成されているのに、そのルートテーブルを変更するのではなく、カスタムルートテーブルを作成するのかというと、「メインルートテーブル」はルートテーブルが関連付けられていないサブネットすべてに、自動的に適用されるといった特性を持っているからです。

そのためメインルートテーブルを編集してしまうと、想定していないサブネットにインターネットへの通信を許可したデフォルトルートの設定の関連付けが行われてしまう可能性があるので、カスタムルートテーブルを作成しています。

ここの話は少しややこしいので、理解できない人は、カスタムルートテーブルを作るんだなーくらいの認識で大丈夫です。ちなみに以下のブログがわかりやすかったので興味がある方は、どうぞ。

ルートテーブルにデフォルトルートを追加しPublicサブネットをルートテーブルに関連付ける

まずルートテーブルに0.0.0.0/0のデフォルトルートを追加します。そのルートテーブルをPublicサブネットに関連付けます。今までの設定方法と変わりはありません。

ルートテーブル関連をCloud Formationでデプロイしよう

それでは、同様に、追加したyamlファイルをCloudFormationの変更を使ってデプロイしていきましょう。

デプロイが成功したらVPCを確認してみます。TestVPC-PublicRouteが作成されています。

TestVPC-PublicRouteのルートを確認してみるとしっかりと0.0.0.0/0が追加されています。

またTestVPC-PublicRouteのサブネットの関連付けも確認します。

以下のように作成したルートテーブルがTestVPC-PublicSubnetに関連付けされていりることがわかります。

これでルートテーブル関連の設定が終わりました。

EC2インスタンスとセキュリティグループを設定しよう

ようやくEC2インスタンスをデプロイするところまで来ました。

EC2インスタンスをデプロイするためのコードは以下の通りです。追加する場所が分かりづらいので、全てのコードを載せておきます。今回追加したのはParametesの部分とEC2インスタンス作成より下の部分です。

AWSTemplateFormatVersion: '2010-09-09'
#+++++++++++++++++++++++++++++++
# Parametesを追加する
#+++++++++++++++++++++++++++++++
Parameters: 
  KeyName: # キーペアはスタック作成時に選択する
    Description: The EC2 Key Pair to allow SSH access to the instance
    Type: "AWS::EC2::KeyPair::KeyName"

  MyIP: # 自身のパブリックIPアドレスを作成時に入力する
    Description: IP address allowd to access EC2
    Type: String
#+++++++++++++++++++++++++++++++
# TestVPCという名前のVPCを作成する
#+++++++++++++++++++++++++++++++
Resources:
  TestVPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: 10.0.0.0/16
      Tags:
      - Key: Name
        Value: FirstVPC
#+++++++++++++++++++++++++++++++
# Publicサブネットを作成する
#+++++++++++++++++++++++++++++++
  PublicSubnet:
    Type: AWS::EC2::Subnet
    Properties:
      CidrBlock: 10.0.1.0/24
      MapPublicIpOnLaunch: 'true'
      VpcId: !Ref TestVPC
      Tags:
      - Key: Name
        Value: TestVPC-PublicSubnet
#+++++++++++++++++++++++++++++++
# Privateサブネットを作成する
#+++++++++++++++++++++++++++++++
  PrivateSubnet:
    Type: "AWS::EC2::Subnet"
    Properties:
      CidrBlock: 10.0.2.0/24
      MapPublicIpOnLaunch: 'false'
      VpcId: !Ref TestVPC
      Tags:
      - Key: Name
        Value: TestVPC-PrivateSubnet

#+++++++++++++++++++++++++++++++
# インターネットゲートウェイを作成する
#+++++++++++++++++++++++++++++++
  InternetGateway:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags:
      - Key: Name
        Value: FirstVPC-IGW

#+++++++++++++++++++++++++++++++
# インターネットゲートウェイをVPCにアタッチする
#+++++++++++++++++++++++++++++++
  AttachGateway:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      VpcId: !Ref TestVPC
      InternetGatewayId: !Ref InternetGateway

#+++++++++++++++++++++++++++++++
# カスタムルートテーブルを作成する
#+++++++++++++++++++++++++++++++
  PublicRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref TestVPC
      Tags:
      - Key: Name
        Value: TestVPC-PublicRoute

#+++++++++++++++++++++++++++++++
# ルートテーブルにデフォルトルートを追加する
#+++++++++++++++++++++++++++++++
  PublicRoute:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref PublicRouteTable
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref InternetGateway

#+++++++++++++++++++++++++++++++
# Publicサブネットをルートテーブルに関連付ける
#+++++++++++++++++++++++++++++++
  PublicSubnetRouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PublicSubnet
      RouteTableId: !Ref PublicRouteTable

#+++++++++++++++++++++++++++++++
# EC2インスタンス作成 
#+++++++++++++++++++++++++++++++
  EC2: 
    Type: AWS::EC2::Instance
    Properties: 
      ImageId: ami-00d101850e971728d
      KeyName: !Ref KeyName
      InstanceType: t2.micro
      NetworkInterfaces: 
        - AssociatePublicIpAddress: "true"
          DeviceIndex: "0"
          SubnetId: !Ref PublicSubnet
          GroupSet:
            - !Ref EC2SG
      Tags:
          - Key: Name
            Value: ec2-t2

#+++++++++++++++++++++++++++++++
# EC2セキュリティグループ作成
#+++++++++++++++++++++++++++++++

  EC2SG:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupName: ec2-sg-cf
      GroupDescription: Allow SSH and HTTP access only MyIP # SSHとHTTPを許可
      VpcId: !Ref TestVPC
      SecurityGroupIngress:
        # http
        - IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          CidrIp: !Ref MyIP 
        # ssh
        - IpProtocol: tcp
          FromPort: 22
          ToPort: 22
          CidrIp: !Ref MyIP

Outputs:
  EC2PublicIP:
    Value: !GetAtt EC2.PublicIp
    Description: Public IP of EC2 instance

Parametesとは

今回新たに追加したParametesとは何でしょうか?

このParametsは、スタック作成時にマネジメントコンソールから入力した値を受け渡すことができます。

言葉で説明するよりもコンソール画面を見た方がわかりやすいので、以下の画像を見てみてください。

このように、スタック作成時にマネジメントコンソールから値を受け渡すことができます。以下の部分のコードが表示されているイメージです。

Parameters: 
  KeyName: # キーペアはスタック作成時に選択する
    Description: The EC2 Key Pair to allow SSH access to the instance
    Type: "AWS::EC2::KeyPair::KeyName"

  MyIP: # IPアドレスを作成時に入力する
    Description: IP address allowd to access EC2
    Type: String

では、Parametersを使用するメリットは何だと思いますか?

今回、Parametersでは自身のパブリックIPアドレスとKeyペアを入力しています。この部分のコードに入力していて、GitHubなどのインターネット上で閲覧することができる場所に公開してしまったらどうなるでしょうか?セキュリティ上よろしくないですよね。そのため、このような重要な情報はParametersを使用するようにします。

また、作業者によって変えたい情報などもPametersを使用しても良いかもしれませんね。

EC2インスタンスの作成

この設定で見てもらいたいところは、Key Name:!Ref KeyNameの部分です。この部分は先ほど説明したParametersでマネジメントコンソールから入力(選択)された値が代入されます。

セキュリティグループの作成

セキュリティグループでも、CidrIp:!Ref MyIPの部分で、Parametersでマネジメントコンソールから入力された値が代入されます。HTTPとSSHのポートを許可する設定にしています。

Outputsとは?

特定のリソースの属性やスタック内で作成されたリソースへの参照など、リソース情報を簡単に取得できます。

こちらも実際に見た方がわかりやすいので、Outputsの部分をマネジメントコンソールの画面で見てみます。

スタックの出力部分にEC2PublicIPとしてEC2インスタンスのパブリックIPドレスが表示されています。

このEC2PuplicIPというのはcloudFormationで作成したEC2インスタンスのパブリクIPが表示されるように「Value: !GetAtt EC2.PublicIp」と設定したからです。

Outputs:
  EC2PublicIP:
    Value: !GetAtt EC2.PublicIp
    Description: Public IP of EC2 instance

EC2インスタンスとセキュリティグループをデプロイするCloudFormationを実行しよう。

今回Cloud Formationを実行する前に事前準備が2つあるので準備しましょう。

キーペアを発行する

まずは、EC2インスタンスにログインする際に必要なキーペアを発行しましょう。

ネットワーク&セキュリティ→キーペアを選択します。

右上にある「キーペアの作成」を選択します。

今回はCloudFormationと名付けてキーペアを作成しました。

キーペアを作成後プライベートキーを保存することができるのですが、この時にしかプライベートキーを保存することができないので、こちらのキーペアを紛失しないように気をつけてください。

自身のPCのパブリックIPアドレスを確認する

今回は自身のPCからのSSHのみ許可するので自身のPCの使用しているパブリックIPアドレスを調べましょう。

調べるには以下のサイトを使用します。こちらに表示されるIPアドレスをどこかにコピーしておきます。

CloudFormationの実行

これでCloudFormationを実行する準備が整いました。いつものようにスタックの更新を選択します。

スタックの詳細を設定と表示されるのでKeynameの部分で先ほど作成したEC2のキーペア(CloudFormation)を選択しMy IPの部分も先ほど調べたパブリックIPアドレスを入力し、「次へ」を選択します。

My IPの部分のCIDRブロックの入力を⚪︎.⚪︎.⚪︎.⚪︎と入力してしまったため、以下のようなエラーが出ました。

スタックの作成が失敗するとなぜ失敗したのか、イベントから調査することができます。今回は、CIDRブロックの入力を⚪︎.⚪︎.⚪︎.⚪︎/32と入力することで正しくスタックの作成ができました。エラーは英語で表示されますが、コピーして検索すれば、解決策が見つかることが多いです。

それでは、EC2インスタンスが作成されているか、確認してみます。以下のようにEC2インスタンスが作成されていれば成功です。

次にセキュリティグループの確認をします。

そして、セキィティグループのインバウンドルールも作成できていることを確認します。

しっかりとHTTPとSSHのインバウンドルールが許可されていることが確認できました。

NATゲートウェイを作成しよう

続いてNATゲートウェイを作成していきます。NATゲートウェイの作成コードは比較的簡単です。

#+++++++++++++++++++++++++++++++
# NATゲートウェイ用のElasticIPの用意
#+++++++++++++++++++++++++++++++
  NatGatewayEIP:
    Type: AWS::EC2::EIP
    Properties:
      Domain: vpc

#+++++++++++++++++++++++++++++++
# NATゲートウェイの作成
#+++++++++++++++++++++++++++++++
  NatGateway:
    Type: AWS::EC2::NatGateway
    Properties:
      AllocationId:
        Fn::GetAtt:
          - NatGatewayEIP
          - AllocationId
      SubnetId: !Ref PublicSubnet
      Tags:
        - Key: Name
          Value: NatGateway

こちらの設定でデプロイが完了すると以下のように表示されます。

プライベートサブネットへのEC2インスタンスのデプロイ&プライベートサブネットのルートテーブルの変更設定&セキュリティグループの設定

この後の設定は、ここまで解説してきた内容とほぼ同じなので一気に設定してしまいましょう。

プライベートサブネットのEC2インスタンスの設定

#+++++++++++++++++++++++++++++++
# PrivateサブネットにEC2インスタンスの作成
#+++++++++++++++++++++++++++++++
  PrEC2: 
    Type: AWS::EC2::Instance
    Properties: 
      ImageId: ami-00d101850e971728d
      KeyName: !Ref KeyName
      InstanceType: t2.micro
      NetworkInterfaces: 
        - AssociatePublicIpAddress: "false"
          DeviceIndex: "0"
          SubnetId: !Ref PrivateSubnet
          GroupSet:
            - !Ref EC2SG2
      Tags:
          - Key: Name
            Value: ec2-2-t2

プライベートサブネットのEC2セキュリティグループ作成

#+++++++++++++++++++++++++++++++
# プライベートサブネットのEC2セキュリティグループ作成
#+++++++++++++++++++++++++++++++
  EC2SG2:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupName: ec2-2-sg-cf
      GroupDescription: Allow SSH  
      VpcId: !Ref TestVPC
      SecurityGroupIngress:
        # ssh
        - IpProtocol: tcp
          FromPort: 22
          ToPort: 22
          CidrIp: 0.0.0.0/0

ルートテーブル関連の設定をする

#+++++++++++++++++++++++++++++++
# Privateサブネットにカスタムルートテーブルを作成する
#+++++++++++++++++++++++++++++++
  PrivateRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref TestVPC
      Tags:
      - Key: Name
        Value: TestVPC-PrivateRoute

#+++++++++++++++++++++++++++++++
# PrivateRサブネットのルートテーブルにデフォルトルートを追加する
#+++++++++++++++++++++++++++++++
  PrivateRoute:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref PrivateRouteTable
      DestinationCidrBlock: 0.0.0.0/0
      NatGatewayId: !Ref NatGateway

#+++++++++++++++++++++++++++++++
# Privateサブネットをルートテーブルに関連付ける
#+++++++++++++++++++++++++++++++
  PrivateSubnetRouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PrivateSubnet
      RouteTableId: !Ref PrivateRouteTable

DestinationCidrBlock: 0.0.0.0/0 NatGatewayId: !Ref NatGatewayの部分は、作成したルートテーブルのデフォルトルートをNATゲートウェイに紐づけています。これでサブネット内のEC2インスタンスからインターネット向けの通信はNATゲートウェイを経由してIGW(インターネットゲートウェイ)に向かいます。

反対にインターネットからEC2に向けての通信はNATゲートウェイによって破棄されます。これでEC2からインターネットに向けての通信はOKとなりますが、インターネットからEC2インスタンスに向けての拒否される設定が作れました。(EC2→インターネット→EC2の戻りの通信は許可されるマス)

プライベートサブネット内の設定をデプロイしよう

それでは、スタックの更新を行います。正常にスタックの更新が終了したら、まずはEC2インスタンスがプライベートサブネット内にデプロイされているか確認しましょう。

正常にデプロイされていたら、新たにec2-2-t2という名前のEC2インスタンスが作成されているはずです。

次にセキュリティグループの確認をしてみましょう。こちらはec2-2-sg-cfという名前のセキュリティグループが作成されています。こちらのセキュリティグループではSSHのインバウンドのみ許可されています。

最後にルートテーブルの確認をしましょう。新たにプライベートサブネット用のルートテーブルが作成されていることが確認できます。

そしてこのルートテーブルを確認してみると、0.0.0.0がNATゲートウェイに紐付けられていることがわかります。

あらた

ここまでで、全てのサービスのデプロイが完了しました。もう少しだけ頑張りましょう。

一応ですが、最終的な設定ファイルのコードを置いておきます。

AWSTemplateFormatVersion: '2010-09-09'
#+++++++++++++++++++++++++++++++
# Parametesを追加する
#+++++++++++++++++++++++++++++++
Parameters: 
  KeyName: # キーペアはスタック作成時に選択する
    Description: The EC2 Key Pair to allow SSH access to the instance
    Type: "AWS::EC2::KeyPair::KeyName"

  MyIP: # 自身のパブリックIPアドレスを作成時に入力する
    Description: IP address allowd to access EC2
    Type: String
#+++++++++++++++++++++++++++++++
# TestVPCという名前のVPCを作成する
#+++++++++++++++++++++++++++++++
Resources:
  TestVPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: 10.0.0.0/16
      Tags:
      - Key: Name
        Value: FirstVPC
#+++++++++++++++++++++++++++++++
# Publicサブネットを作成する
#+++++++++++++++++++++++++++++++
  PublicSubnet:
    Type: AWS::EC2::Subnet
    Properties:
      CidrBlock: 10.0.1.0/24
      MapPublicIpOnLaunch: 'true'
      VpcId: !Ref TestVPC
      Tags:
      - Key: Name
        Value: TestVPC-PublicSubnet
#+++++++++++++++++++++++++++++++
# Privateサブネットを作成する
#+++++++++++++++++++++++++++++++
  PrivateSubnet:
    Type: "AWS::EC2::Subnet"
    Properties:
      CidrBlock: 10.0.2.0/24
      MapPublicIpOnLaunch: 'false'
      VpcId: !Ref TestVPC
      Tags:
      - Key: Name
        Value: TestVPC-PrivateSubnet

#+++++++++++++++++++++++++++++++
# インターネットゲートウェイを作成する
#+++++++++++++++++++++++++++++++
  InternetGateway:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags:
      - Key: Name
        Value: FirstVPC-IGW

#+++++++++++++++++++++++++++++++
# インターネットゲートウェイをVPCにアタッチする
#+++++++++++++++++++++++++++++++
  AttachGateway:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      VpcId: !Ref TestVPC
      InternetGatewayId: !Ref InternetGateway

#+++++++++++++++++++++++++++++++
# カスタムルートテーブルを作成する
#+++++++++++++++++++++++++++++++
  PublicRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref TestVPC
      Tags:
      - Key: Name
        Value: TestVPC-PublicRoute

#+++++++++++++++++++++++++++++++
# ルートテーブルにデフォルトルートを追加する
#+++++++++++++++++++++++++++++++
  PublicRoute:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref PublicRouteTable
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref InternetGateway

#+++++++++++++++++++++++++++++++
# Publicサブネットをルートテーブルに関連付ける
#+++++++++++++++++++++++++++++++
  PublicSubnetRouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PublicSubnet
      RouteTableId: !Ref PublicRouteTable

#+++++++++++++++++++++++++++++++
# EC2インスタンス作成 
#+++++++++++++++++++++++++++++++
  EC2: 
    Type: AWS::EC2::Instance
    Properties: 
      ImageId: ami-00d101850e971728d
      KeyName: !Ref KeyName
      InstanceType: t2.micro
      NetworkInterfaces: 
        - AssociatePublicIpAddress: "true"
          DeviceIndex: "0"
          SubnetId: !Ref PublicSubnet
          GroupSet:
            - !Ref EC2SG
      Tags:
          - Key: Name
            Value: ec2-t2

#+++++++++++++++++++++++++++++++
# EC2セキュリティグループ作成
#+++++++++++++++++++++++++++++++

  EC2SG:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupName: ec2-sg-cf
      GroupDescription: Allow SSH access only MyIP # 
      VpcId: !Ref TestVPC
      SecurityGroupIngress:
        # ssh
        - IpProtocol: tcp
          FromPort: 22
          ToPort: 22
          CidrIp: !Ref MyIP

#+++++++++++++++++++++++++++++++
# NATゲートウェイ用のElasticIPの用意
#+++++++++++++++++++++++++++++++
  NatGatewayEIP:
    Type: AWS::EC2::EIP
    Properties:
      Domain: vpc

#+++++++++++++++++++++++++++++++
# NATゲートウェイの作成
#+++++++++++++++++++++++++++++++
  NatGateway:
    Type: AWS::EC2::NatGateway
    Properties:
      AllocationId:
        Fn::GetAtt:
          - NatGatewayEIP
          - AllocationId
      SubnetId: !Ref PublicSubnet
      Tags:
        - Key: Name
          Value: NatGateway
          
#+++++++++++++++++++++++++++++++
# PrivateサブネットにEC2インスタンスの作成
#+++++++++++++++++++++++++++++++
  PrEC2: 
    Type: AWS::EC2::Instance
    Properties: 
      ImageId: ami-00d101850e971728d
      KeyName: !Ref KeyName
      InstanceType: t2.micro
      NetworkInterfaces: 
        - AssociatePublicIpAddress: "false"
          DeviceIndex: "0"
          SubnetId: !Ref PrivateSubnet
          GroupSet:
            - !Ref EC2SG2
      Tags:
          - Key: Name
            Value: ec2-2-t2

#+++++++++++++++++++++++++++++++
# プライベートサブネットのEC2セキュリティグループ作成
#+++++++++++++++++++++++++++++++
  EC2SG2:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupName: ec2-2-sg-cf
      GroupDescription: Allow SSH  
      VpcId: !Ref TestVPC
      SecurityGroupIngress:
        # ssh
        - IpProtocol: tcp
          FromPort: 22
          ToPort: 22
          CidrIp: 0.0.0.0/0
#+++++++++++++++++++++++++++++++
# Privateサブネットにカスタムルートテーブルを作成する
#+++++++++++++++++++++++++++++++
  PrivateRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref TestVPC
      Tags:
      - Key: Name
        Value: TestVPC-PrivateRoute

#+++++++++++++++++++++++++++++++
# PrivateRサブネットのルートテーブルにデフォルトルートを追加する
#+++++++++++++++++++++++++++++++
  PrivateRoute:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref PrivateRouteTable
      DestinationCidrBlock: 0.0.0.0/0
      NatGatewayId: !Ref NatGateway

#+++++++++++++++++++++++++++++++
# Privateサブネットをルートテーブルに関連付ける
#+++++++++++++++++++++++++++++++
  PrivateSubnetRouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PrivateSubnet
      RouteTableId: !Ref PrivateRouteTable

Outputs:
  EC2PublicSubnetPublicIP:
    Value: !GetAtt EC2.PublicIp
    Description: PublicSubnet Public IP of EC2 instance

SSH接続してみよう

それでは最後に、プライベートサブネットのEC2に自身のEC2インスタンスからSSH接続してさらにそこからプライベートサブネットの中にあるEC2にSSHできるか確認してみます。

踏み台サーバにSSHしよう

まずEC2インスタンスのダッシュボードでec2-t2の「インスタンスID」の部分を選択します。これがパブリックサブネとに配置されているEC2インスタンスです。

次にインスタンスの概要の横にある「接続」を選択します

SSHのタブに行き(例)の部分をメモ帳にコピーしておいてください。

次に、Terminalから以前ダウンロードしたキーペアの.pemファイルを保存しているフォルダまで移動します。

そのフォルダで以下のコマンドを実行してください。これは鍵の権限を変更しています。詳しくはLPICを勉強してください笑

chmod 400 "CloudFormation-test.pem"

次に、先ほどコピーした以下のコマンドを使用します。

ssh -i "CloudFormation-test.pem" ec2-user@<パブリックサブネットのEC2インスタンスのパブリックIP>

途中でyes入力し、以下のように表示されていればログイン成功です。一度exitでEC2インスタンスから抜けてください。

This key is not known by any other names
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '35.78.183.255' (ED25519) to the list of known hosts.

       __|  __|_  )
       _|  (     /   Amazon Linux 2 AMI
      ___|\___|___|

次にEC2インスタンスにこのキーペアを自身のPCから転送してあげます。コピーのために以下のコマンドを実行してください。Windowsの方はWinScp等を使用してください。

scp -i CloudFormation-test.pem CloudFormation-test.pem ec2-user@<パブリックサブネットのEC2インスタンスのパブリックIP>:/tmp
CloudFormation-test.pem                       100% 1678    86.9KB/s   00:00   

再度EC2インスタンスにログインして、以下のコマンドでキーペアが自身のPCから転送されているか確認します。

[ec2-user@ip-10-0-1-155 ~]$ cd /tmp
[ec2-user@ip-10-0-1-155 tmp]$ ls -l
合計 4
-rw------- 1 ec2-user ec2-user 1678  2月 27 10:58 CloudFormation-test.pem
drwx------ 3 root     root       17  2月 27 10:34 systemd-private-bf3d122ac3584505baf03f7801f5f73f-chronyd.service-pxfhZY

しっかりと転送されていることが確認できると思います。

最後にこのキーペアを使用してパブリックサブネットのEC2からプライベートサブネットのEC2へログインしてみましょう。ログインする際のコマンドは、キーぺが保存されている/tmpのフォルダで以下のコマンドを実行します。

ssh -i "CloudFormation-test.pem" ec2-user@<プライベートサブネットのEC2インスタンスのプライベートIP>

以下のように表示されてログインされていれば成功です。

Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '10.0.2.242' (ECDSA) to the list of known hosts.

       __|  __|_  )
       _|  (     /   Amazon Linux 2 AMI
      ___|\___|___|

https://aws.amazon.com/amazon-linux-2/

最後にお片付け

今回CloudFormationを用いて作成したサービス達は簡単に削除することができるので、最後にその方法を共有します。

まずCloudForamtionのスタック→削除を選択します。

次にもう一度「削除」を選択します。

しばらく待つと以下の画像のようにスタックが削除されます。スタックが削除されるのと同時にデプロイしているサービスも削除されます。

あらた

これで後片付けも完了です。お疲れ様でした!

まとめ

いかがだったでしょうか?CloudFormationのハンズオンは楽しんでいただけましたか?

少しでもCloudFomationとお友達になれたと感じていただけたら幸いです。

最後まで、ハンズオンしていただけた方がもしいたら、Twiterで報告していただけたら嬉しいです笑

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

コメント

コメントする

目次