AWS環境のコード管理 AWS CloudFormationでWebシステムを構築する【AWS Hands-on for Beginners】

# はじめに
今回は【AWS環境のコード管理 AWS CloudFormationでWebシステムを構築する】のハンズオン資料を見ながら、CloudFormationでWebシステムを構築していきたいと思います。

https://pages.awscloud.com/JAPAN-event-OE-Hands-on-for-Beginners-cfn-2022-reg-event.html?trk=aws_introduction_page

# 学習するサービス
- AWS CloudFormation 
- AWS Cloud9
- ELB
- RDS

# 【AWS環境のコード管理 AWS CloudFormationでWebシステムを構築する】の概要
このハンズオンはCloudFormationを使って、以下のシステムを構築します。
![hanson2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2820434/6f3ec758-732b-9e05-5832-ff26ebca5be8.png)



このハンズオンは以下の構成になっております。
|  | Session | 分:秒 |
| ---- | ---- |---- |
|1|ハンズオンで構築する構成とクラウドにおける構成管理について|13:12|
|2|CloudFormation概要|08:29|
|3|開発環境の構築|11:41|
|4|テンプレートの実行方法、VPCの作成方法|11:17|
|5|EC2の作成 その1|12:59|
|6|EC2の作成 その2|08:55|
|7|RDS、ELBの作成、スタックのライフサイクル・分割|07:11|
|8|まとめ、削除|04:19|

座学動画についてはこの記事では触れませんが、とても分かりやすいと思うので
気になった方はぜひ[AWS Hands-on for Beginners - AWS環境のコード管理 AWS CloudFormationでWebシステムを構築する](https://pages.awscloud.com/JAPAN-event-OE-Hands-on-for-Beginners-cfn-2022-reg-event.html?trk=aws_introduction_page) をご覧ください。

# ハンズオン実践
細かいやり方や設定については省略させていただきます。
詳細については、ハンズオン動画をご覧いただければと思います。

## 3.開発環境の構築
Cloud9を開発環境として、構築します。
1.すべてデフォルト値でCloud9のインスタンスを作成する。
2.yaml形式で記述するため、yaml形式での一般的なインデントの深さにするためCloud9の設定でSoft Tabsをスペース4つから2つに変更する。

## 4.テンプレートの実行方法、VPCの作成方法
マネジメントコンソールからVPC用のスタックを作成したり、AWSCLIを使用してスタックの更新を行っていきます。

1. マネジメントコンソールからテンプレート「01_vpc.yml」を使ってVPCのスタック「handson-cfn」を作成する。
2. Cloud9にテンプレートファイルをアップロードする。
3. Cloud9にて、テンプレート「01_vpc.yml」のネームタグの値を変更する。
4. AWS CLIを使用してスタック「handson-cfn」を更新する。

CloudFormationのマネジメントコンソールにて、下記のテンプレート「01_vpc.yml」を使用して、VPCのスタック「handson-cfn」を作成しました。※こちらのテンプレートはハンズオンで提供されたyamlファイルです。
<details>
<summary>テンプレートを表示</summary>

```yaml:01_vpc.yml
AWSTemplateFormatVersion: 2010-09-09
Description: Hands-on template for VPC

Resources:
  CFnVPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: 10.0.0.0/16
      InstanceTenancy: default
      EnableDnsSupport: true
      EnableDnsHostnames: true
      Tags:
        - Key: Name
          Value: handson-cfn

  PublicSubnet1:
    Type: AWS::EC2::Subnet
    Properties:
      CidrBlock: 10.0.0.0/24
      VpcId: !Ref CFnVPC
      AvailabilityZone: !Select [ 0, !GetAZs ]
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: PublicSubnet1

  PublicSubnet2:
    Type: AWS::EC2::Subnet
    Properties:
      CidrBlock: 10.0.1.0/24
      VpcId: !Ref CFnVPC
      AvailabilityZone: !Select [ 1, !GetAZs ]
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: PublicSubnet2

  PrivateSubnet1:
    Type: AWS::EC2::Subnet
    Properties:
      CidrBlock: 10.0.2.0/24
      VpcId: !Ref CFnVPC
      AvailabilityZone: !Select [ 0, !GetAZs ]
      Tags:
        - Key: Name
          Value: PrivateSubnet1

  PrivateSubnet2:
    Type: AWS::EC2::Subnet
    Properties:
      CidrBlock: 10.0.3.0/24
      VpcId: !Ref CFnVPC
      AvailabilityZone: !Select [ 1, !GetAZs ]
      Tags:
        - Key: Name
          Value: PrivateSubnet2

  CFnVPCIGW:
    Type: AWS::EC2::InternetGateway
    Properties: 
      Tags:
        - Key: Name
          Value: handson-cfn

  CFnVPCIGWAttach:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties: 
      InternetGatewayId: !Ref CFnVPCIGW
      VpcId: !Ref CFnVPC

  PublicRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref CFnVPC
      Tags:
        - Key: Name
          Value: Public Route

  PublicRoute:
    Type: AWS::EC2::Route
    DependsOn: CFnVPCIGW
    Properties:
      RouteTableId: !Ref PublicRouteTable
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref CFnVPCIGW

  PublicSubnet1Association:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PublicSubnet1
      RouteTableId: !Ref PublicRouteTable

  PublicSubnet2Association:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PublicSubnet2
      RouteTableId: !Ref PublicRouteTable

Outputs:
  VPCID:
    Description: VPC ID
    Value: !Ref CFnVPC
    Export:
      Name: !Sub ${AWS::StackName}-VPCID

  PublicSubnet1:
    Description: PublicSubnet1
    Value: !Ref PublicSubnet1
    Export:
      Name: !Sub ${AWS::StackName}-PublicSubnet1

  PublicSubnet2:
    Description: PublicSubnet2
    Value: !Ref PublicSubnet2
    Export:
      Name: !Sub ${AWS::StackName}-PublicSubnet2

  PrivateSubnet1:
    Description: PrivateSubnet1
    Value: !Ref PrivateSubnet1
    Export:
      Name: !Sub ${AWS::StackName}-PrivateSubnet1

  PrivateSubnet2:
    Description: PrivateSubnet2
    Value: !Ref PrivateSubnet2
    Export:
      Name: !Sub ${AWS::StackName}-PrivateSubnet2
```

</details>


以下のコマンドをCloud9のターミナルで実行して、テンプレートファイルがあるフォルダに移動します。
```bash
cd cfn
```
テンプレート「01_vpc.yml」の```Resources``` ```PublicSubnet1``` ```Properties``` ```Tags```の```Value```を```PublecSubnet1```から```PublecSubnet1-update```に変更する。

変更したテンプレート「01_vpc.yml」が正しく記述されているか検証するため以下のコマンドを実行します。
```bash
aws cloudformation validate-template --template-body file://01_vpc.yml
```

以下のようにエラーが発生することなくレスポンスが返ってこれば、テンプレートは正しく記述されています。
```json
{
    "Description": "Hands-on template for VPC", 
    "Parameters": []
}
```

続いて、スタックの更新を行っていきます。
以下のコマンドを実行し、スタックの更新を行います。
```bash
aws cloudformation update-stack --stack-name handson-cfn --template-body file://01_vpc.yml
```

以下のようにエラーが発生することなくレスポンスが返ってこれば無事にスタックの更新が開始されています。
```json
{
    "StackId": "arn:aws:cloudformation:ap-northeast-1:571504036579:stack/handson-cfn/67ff5ad0-3863-11ee-a561-0673bbff0e97"
}
```

マネジメントコンソールでも、スタックの更新がされているか確認していきます。
マネジメントコンソールでCloudFormationを開き、「スタック」から、今回作成したスタック「handson-cfn」をクリックします。
![CloudFormation - スタック handson-cfn - Google Chrome 2023_08_12 15_57_45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2820434/1ae9c186-e886-0ffd-763e-31a2a8603579.png)

するとスタックの詳細が表示されるので、「リソース」から「PublicSubnet1」の物理IDをクリックします。
![CloudFormation - スタック handson-cfn - Google Chrome 2023_08_12 15_56_25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2820434/4389abf4-934d-2f43-d479-f6301d02f4a9.png)

無事に「PublicSubnet1」のネームタグの値が「PublecSubnet1-update」に更新されているのが確認できました。
![Qiita - Google Chrome 2023_08_12 16_11_49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2820434/68f3821d-b60c-1879-144a-b94457bf31f9.png)

## 5. EC2の作成 その1
[テンプレートの実行方法、VPCの作成方法](#4テンプレートの実行方法vpcの作成方法)で作成したVPCにEC2を作成します。

1.AWS CLIを使用してスタック「handson-cfn-ec2」を作成しEC2を起動する。
2.EC2はデフォルトのVPCで起動されているため、作成したVPC「handson-cfn」の「PublicSubnet1-update」で起動するように、テンプレート「02_ec2.yml」を修正する。
3.インスタンス起動時、スクリプトを実行するためにテンプレート「02_ec2.yml」にUserDataプロパティを追加する。
4.AWS CLIにて、スタック「handson-cfn-ec2」の更新を行う。

Cloud9のターミナルで以下のコマンドを実行して、テンプレートファイルがあるフォルダに移動します。
```bash
cd cfn
```

下記のテンプレート「02_ec2.yml」を使用して、VPCのスタック「handson-cfn-ec2」を作成します。※こちらのテンプレートはハンズオンで提供されたyamlファイルです。

<details>
<summary>テンプレートを表示</summary>

```yaml:handson-cfn-ec2
AWSTemplateFormatVersion: 2010-09-09
Description: Hands-on template for EC2

Parameters:
  VPCStack:
    Type: String
    Default: handson-cfn
  EC2AMI:
    Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>
    Default: /aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2

Resources:
  EC2WebServer01:
    Type: AWS::EC2::Instance
    Properties:
      ImageId: !Ref EC2AMI
      InstanceType: t2.micro

Outputs:
  EC2WebServer01:
    Value: !Ref EC2WebServer01
    Export:
      Name: !Sub ${AWS::StackName}-EC2WebServer01
```

</details>

以下のコマンドを実行して、EC2用のスタック「handson-cfn-ec2」を作成します。
```bash
aws cloudformation create-stack --stack-name handson-cfn-ec2 --template-body file://02_ec2.yml 
```
以下のようにエラーが発生することなくレスポンスが返ってこれば無事にスタックの作成が開始されています。
```json
{
    "StackId": "arn:aws:cloudformation:ap-northeast-1:571504036579:stack/handson-cfn-ec2/f453c160-38e5-11ee-b460-06ad44770f53"
}
```

CloudFormationのマネジメントコンソールでも、無事にスタックが作成されていることが確認できました。
![AWS Hands-on for Beginners AWS 環境のコード管理 AWS CloudFormationで Web システムを構築する _ AWS Webinar - Google Chrome 2023_08_12 17_25_01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2820434/92c718e8-d518-14a4-56f2-b429423b3299.png)

インスタンスも無事に起動できています。
![フォト 2023_08_12 17_28_59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2820434/6021b7e7-1a6c-8238-b3b0-a7250f6841fd.png)

作成したEC2を作成したVPC「handson-cfn」のサブネット「PublicSubnet1-update」で起動するように、テンプレート「02_ec2.yml」を修正していきます。

CloudFormationのマネジメントコンソールから、スタック「handson-cfn」を選択し、「出力」タブを選択します。「PublicSubnet1」の「値」をコピーします。
![フォト 2023_08_12 18_17_27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2820434/e3cd8204-05e8-e7d1-e149-7939ec2d7e55.png)

テンプレート「02_ec2.yml」に```SubnetId```として先ほどコピーした「PublicSubnet1」の「値」を指定します。

```yaml:02_ec2.yml
Resources:
  EC2WebServer01:
    Type: AWS::EC2::Instance
    Properties:
      ImageId: !Ref EC2AMI
      InstanceType: t2.micro
      SubnetId: subnet-06bb6ddf7db2d641d  
```

以下のコマンドを実行し、テンプレートの検証を行います。
```bash
aws cloudformation validate-template --template-body file://02_ec2.yml
```

レスポンスとして下記のように、エラーが返ってこなければテンプレートは正しく記述されています。

```json:Respons
{
    "Description": "Hands-on template for EC2", 
    "Parameters": [
        {
            "DefaultValue": "handson-cfn", 
            "NoEcho": false, 
            "ParameterKey": "VPCStack"
        }, 
        {
            "DefaultValue": "/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2", 
            "NoEcho": false, 
            "ParameterKey": "EC2AMI"
        }
    ]
}
```

続いて、EC2インスタンス起動時にスクリプトを実行するように、テンプレート「02_ec2.yml」に```UserData```プロパティを追加していきます。
また、追加したスクリプトをエンコードするため、組み込み関数Fn::Base64の短縮形である```!Base64```を記述しています。

```yaml:02_ec2.yml
Resources:
  EC2WebServer01:
    Type: AWS::EC2::Instance
    Properties:
      ImageId: !Ref EC2AMI
      InstanceType: t2.micro
      SubnetId: subnet-06bb6ddf7db2d641d  
      UserData: !Base64 |
        #! /bin/bash
        yum update -y
        amazon-linux-extras install php7.2 -y
        yum -y install mysql httpd php-mbstring php-xml
        
        wget http://ja.wordpress.org/latest-ja.tar.gz -P /tmp/
        tar zxvf /tmp/latest-ja.tar.gz -C /tmp
        cp -r /tmp/wordpress/* /var/www/html/
        touch /var/www/html/.check_alive
        chown apache:apache -R /var/www/html
        
        systemctl enable httpd.service
        systemctl start httpd.service
```

以下のコマンドを実行し、テンプレートの検証を行います。
```bash
aws cloudformation validate-template --template-body file://02_ec2.yml
```
下記のようにエラーが返ってこなければテンプレートは正しく記述されています。
```json:Respons
{
    "Description": "Hands-on template for EC2", 
    "Parameters": [
        {
            "DefaultValue": "handson-cfn", 
            "NoEcho": false, 
            "ParameterKey": "VPCStack"
        }, 
        {
            "DefaultValue": "/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2", 
            "NoEcho": false, 
            "ParameterKey": "EC2AMI"
        }
    ]
}
```

変更したテンプレートをスタックに反映させるため、AWS CLIにてコマンドを実行し、スタック「handson-cfn-ec2」の更新を行っていきます。

```bash
aws cloudformation update-stack --stack-name handson-cfn-ec2 --template-body file://02_ec2.yml
```
以下のようにエラーが発生することなくレスポンスが返ってこれば無事にスタックの更新が開始されています。
```json
{
    "StackId": "arn:aws:cloudformation:ap-northeast-1:571504036579:stack/handson-cfn-ec2/f453c160-38e5-11ee-b460-06ad44770f53"
}
```
CloudFormationのマネジメントコンソールでもスタックが更新されているか確認できました。
![AWS Hands-on for Beginners AWS 環境のコード管理 AWS CloudFormationで Web システムを構築する _ AWS Webinar - Google Chrome 2023_08_12 22_38_40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2820434/bdf6ce1e-2f4d-0707-0966-1257e7fb1256.png)

無事に指定したVPC「handson-cfn」のサブネット「PublicSubnet1-update」でEC2が起動されていることを確認できました。
![インスタンスの詳細 _ EC2 Management Console - Google Chrome 2023_08_12 22_42_54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2820434/38b09cc9-431d-81a4-197d-3b1c859ebd97.png)

ユーザーデータについても確認してみます。
「アクション」→「インスタンスの設定」→「ユーザーデータ」を選択します。
![インスタンスの詳細 _ EC2 Management Console - Google Chrome 2023_08_12 22_56_01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2820434/1f0f2f34-e122-37cf-7365-4bb8841da696.png)

無事にユーザーデータを確認できました。
![インスタンスの詳細 _ EC2 Management Console - Google Chrome 2023_08_12 22_56_08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2820434/4b01c75b-8434-49b5-988e-5065d5488360.png)

## 5. EC2の作成 その2
EC2のセキュリティーグループ作成・設定をしていきます。

1. セキュリティーグループ「EC2SG」を作成するためにテンプレート「02_ec2.yml」の```Resource```に値を追加します。
2. AWS CLIを使ってスタック「handson-cfn-ec2」を更新し、セキュリティーグループを作成する。
3. 作成したセキュリティーグループをEC2に関連付けるためにテンプレート「02_ec2.yml」を修正する。
4. AWS CLIを使ってスタック「handson-cfn-ec2」を更新し、セキュリティーグループの関連付けを行う。 

セキュリティーグループのプロパティとして、「VPCId」を使用するのでCloudFormationのマネジメントコンソールからスタック「handson-cfn」→「VPCID」の「値」をコピーしておきます。
![CloudFormation - スタック handson-cfn - Google Chrome 2023_08_13 0_19_48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2820434/5de320d7-c932-89b2-ca19-6f24a1c64ec0.png)

テンプレート「02_ec2.yml」の`Resource`セクションに以下のようにプロパティを追加します。
ここで`VpcId`に先ほどコピーした「VPCID」を指定します。
```yaml:02_ec2.yml
  EC2SG:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: sg for web server
      VpcId: vpc-03be38ee3e4427d3c
      SecurityGroupIngress:
        - IpProtocol: tcp
          CidrIp: 10.0.0.0/16
          FromPort: 80
          ToPort: 80
```



以下のコマンドを実行し、テンプレートの検証を行います。
```bash
aws cloudformation validate-template --template-body file://02_ec2.yml
```
下記のようにエラーが返ってこなければテンプレートは正しく記述されています。
```json:Respons
{
    "Description": "Hands-on template for EC2", 
    "Parameters": [
        {
            "DefaultValue": "handson-cfn", 
            "NoEcho": false, 
            "ParameterKey": "VPCStack"
        }, 
        {
            "DefaultValue": "/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2", 
            "NoEcho": false, 
            "ParameterKey": "EC2AMI"
        }
    ]
}
```

変更したテンプレートをスタックに反映させるため、スタック「handson-cfn-ec2」の更新を行っていきます。
AWS CLIにて以下のコマンドを実行します。

```bash
aws cloudformation update-stack --stack-name handson-cfn-ec2 --template-body file://02_ec2.yml
```
以下のようにエラーが発生することなくレスポンスが返ってこれば無事にスタックの更新が開始されています。
```json
{
    "StackId": "arn:aws:cloudformation:ap-northeast-1:571504036579:stack/handson-cfn-ec2/f453c160-38e5-11ee-b460-06ad44770f53"
}
```
スタックの「リソース」タブからもセキュリティーグループが作成されているのが確認できます。
![CloudFormation - スタック handson-cfn-ec2 - Google Chrome 2023_08_13 1_23_18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2820434/b727bfa9-f7f9-7b42-792b-be20e20ef505.png)

インバウンドルールの許可設定も無事に作成されています。
![CloudFormation - スタック handson-cfn-ec2 - Google Chrome 2023_08_13 1_23_46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2820434/a84c98f4-a253-13d1-427c-60d57347eb30.png)



セキュリティーグループ「EC2SG」をEC2に関連付けます。
テンプレート「02_ec2.yml」の```Resource``` ```EC2WebServer01``` ```Properties```に```SecurityGroupIds```プロパティの値を追加します。
組み込み関数を使い、セキュリティーグループ「EC2SG」のIDを指定します。
```yaml:02_ec2.yml
SecurityGroupIds:
  - !Ref EC2SG
```


以下のコマンドを実行し、テンプレートの検証を行います。
```bash
aws cloudformation validate-template --template-body file://02_ec2.yml
```
下記のようにエラーが返ってこなければテンプレートは正しく記述されています。
```json:Respons
{
    "Description": "Hands-on template for EC2", 
    "Parameters": [
        {
            "DefaultValue": "handson-cfn", 
            "NoEcho": false, 
            "ParameterKey": "VPCStack"
        }, 
        {
            "DefaultValue": "/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2", 
            "NoEcho": false, 
            "ParameterKey": "EC2AMI"
        }
    ]
}
```

変更したテンプレートをスタックに反映させるため、スタック「handson-cfn-ec2」の更新を行っていきます。
AWS CLIにて以下のコマンドを実行します。

```bash
aws cloudformation update-stack --stack-name handson-cfn-ec2 --template-body file://02_ec2.yml
```
以下のようにエラーが発生することなくレスポンスが返ってこれば無事にスタックの更新が開始されています。
```json
{
    "StackId": "arn:aws:cloudformation:ap-northeast-1:571504036579:stack/handson-cfn-ec2/f453c160-38e5-11ee-b460-06ad44770f53"
}
```

EC2のマネジメントコンソールからも無事に関連付けできていることが確認できました。
![CloudFormation - スタック handson-cfn-ec2 - Google Chrome 2023_08_13 1_47_05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2820434/edfbd216-625f-9653-493d-07f460f0ea79.png)

## 7.RDS、ELBの作成、スタックのライフサイクル・分割
AWS CLIを使用して、RDSとELBのスタックを作成していきます。

1. AWS CLIを使ってRDSのスタック「handson-cfn-rds」を作成する。
2. AWS CLIを使ってELBのスタック「handson-cfn-elb」を作成する。
3. ELBからEC2に接続し、EC2にWordPressをインストールし、WordPressにログインする。

下記のテンプレート「03_rds.yml」を使用します。
```yaml:03_rds.yml
AWSTemplateFormatVersion: 2010-09-09
Description: Hands-on template for RDS

Parameters:
  VPCStack:
    Type: String
    Default: handson-cfn
  DBUser:
    Type: String
    Default: dbmaster
  DBPassword:
    Type: String
    Default: H&ppyHands0n
    NoEcho: true

  
Resources:
  DBInstance:
    Type: AWS::RDS::DBInstance
    DeletionPolicy: Delete
    Properties:
      DBInstanceClass: db.t2.micro
      AllocatedStorage: "10"
      StorageType: gp2
      Engine: MySQL
      MasterUsername: !Ref DBUser
      MasterUserPassword: !Ref DBPassword
      DBName: wordpress
      BackupRetentionPeriod: 0
      DBSubnetGroupName: !Ref DBSubnetGroup
      VPCSecurityGroups:
        - !Ref DBSecurityGroup

  DBSubnetGroup: 
    Type: AWS::RDS::DBSubnetGroup
    Properties: 
      DBSubnetGroupDescription: DB Subnet Group for Private Subnet
      SubnetIds: 
        - Fn::ImportValue: !Sub ${VPCStack}-PrivateSubnet1
        - Fn::ImportValue: !Sub ${VPCStack}-PrivateSubnet2

  DBSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties: 
      GroupDescription: !Sub ${AWS::StackName}-MySQL
      VpcId:
        Fn::ImportValue: !Sub ${VPCStack}-VPCID
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 3306
          ToPort: 3306
          CidrIp: 10.0.0.0/16


Outputs:
  DBEndpoint:
    Value: !GetAtt DBInstance.Endpoint.Address
    Export:
      Name: !Sub ${AWS::StackName}-DBEndpoint

```
下記のテンプレート「04_elb.yml」を使用します。
```yaml:04_elb.yml
AWSTemplateFormatVersion: 2010-09-09
Description: Hands-on template for ALB

Parameters:
  VPCStack:
    Type: String
    Default: handson-cfn
  EC2Stack:
    Type: String
    Default: handson-cfn-ec2

Resources:
  FrontLB:
    Type: AWS::ElasticLoadBalancingV2::LoadBalancer
    Properties:
      Name: !Ref AWS::StackName
      Subnets:
        - Fn::ImportValue: !Sub ${VPCStack}-PublicSubnet1
        - Fn::ImportValue: !Sub ${VPCStack}-PublicSubnet2
      SecurityGroups: 
        - !Ref SecurityGroupLB

  FrontLBListener:
    Type: AWS::ElasticLoadBalancingV2::Listener
    Properties:
      LoadBalancerArn: !Ref FrontLB
      Port: 80
      Protocol: HTTP 
      DefaultActions: 
        - Type: forward
          TargetGroupArn: !Ref FrontLBTargetGroup

  FrontLBTargetGroup:
    Type: AWS::ElasticLoadBalancingV2::TargetGroup
    Properties:
      Name: !Sub ${AWS::StackName}-tg
      VpcId:
        Fn::ImportValue: !Sub ${VPCStack}-VPCID
      Port: 80
      Protocol: HTTP
      HealthCheckPath: /.check_alive
      Targets:
        - Id:
            Fn::ImportValue: !Sub ${EC2Stack}-EC2WebServer01

  SecurityGroupLB:
    Type: AWS::EC2::SecurityGroup
    Properties: 
      GroupDescription: !Ref AWS::StackName
      VpcId:
        Fn::ImportValue: !Sub ${VPCStack}-VPCID
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          CidrIp: 0.0.0.0/0

Outputs:
  FrontLBEndpoint:
    Value: !GetAtt FrontLB.DNSName
    Export:
      Name: !Sub ${AWS::StackName}-Endpoint
```

Cloud9のターミナルで以下のコマンドを実行して、テンプレートファイルがあるフォルダに移動します。
```bash
cd cfn
```
以下のコマンドを実行し、テンプレート「03_rds.yml」からRDSのスタック「handson-cfn-rds」を作成します。
```bash
aws cloudformation create-stack --stack-name handson-cfn-rds --template-body file://03_rds.yml 
```
以下のようにエラーが発生することなくレスポンスが返ってこれば無事にスタックの作成が開始されています。
```json
{
    "StackId": "arn:aws:cloudformation:ap-northeast-1:571504036579:stack/handson-cfn-rds/d10689f0-398e-11ee-a250-0e98c60575d9"
}
```
以下のコマンドを実行し、テンプレート「03_rds.yml」からRDSのスタック「handson-cfn-elb」を作成します。
```bash
aws cloudformation create-stack --stack-name handson-cfn-elb --template-body file://04_elb.yml
```
以下のようにエラーが発生することなくレスポンスが返ってこれば無事にスタックの作成が開始されています。
```json
{
    "StackId": "arn:aws:cloudformation:ap-northeast-1:571504036579:stack/handson-cfn-elb/e550a170-398e-11ee-a9fc-0a5a4ab5f0af"
}
```
CloudFormationのコンソールでも無事にリソースが作成されているのが確認できました。
![AWS Hands-on for Beginners AWS 環境のコード管理 AWS CloudFormationで Web システムを構築する _ AWS Webinar - Google Chrome 2023_08_13 14_18_07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2820434/98434b75-2bf3-8af4-0ed8-69bdf0615ab7.png)
![CloudFormation - スタック handson-cfn-elb - Google Chrome 2023_08_13 14_17_36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2820434/fd8ae401-8d79-911f-72da-ecfb1070fab5.png)

	handson-cfn-rds-dbinstance-nhqsw4zvmtpa.cbfpkicusoxk.ap-northeast-1.rds.amazonaws.com
    handson-cfn-rds-dbinstance-dx0lmesvfalc.cbfpkicusoxk.ap-northeast-1.rds.amazonaws.com
dbmaster
H&ppyHands0n

続いて、EC2にWordPressをインストールしていきます。
後ほど必要となる「DBEndPoint」と「DBUser」、「DBPassword」を取得していきます。

CloudFormationのコンソールからスタック「handson-cfn-rds」を選択し、「出力」タブのキー「DBEndpoint」のURLをコピーします。
![AWS Hands-on for Beginners AWS 環境のコード管理 AWS CloudFormationで Web システムを構築する _ AWS Webinar - Google Chrome 2023_08_13 14_30_14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2820434/74e10181-d1b3-fc2b-a62d-4feb93466291.png)

テンプレート「03_rds.yml」の`DBUser`の「dbmaster」と`DBPassword`の「H&ppyHands0n」をコピーします。
![CloudFormation - スタック handson-cfn-rds - Google Chrome 2023_08_13 14_39_01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2820434/ee84192b-89cf-185d-e6b4-905f51ca3298.png)

CloudFormationのコンソールに戻り、スタック「handson-cfn-elb」を選択し、「出力」タブからelbのURLを開きます。
![AWS Hands-on for Beginners AWS 環境のコード管理 AWS CloudFormationで Web システムを構築する _ AWS Webinar - Google Chrome 2023_08_13 15_02_10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2820434/d73e1dd8-edf7-4910-8928-bc1ed9e0addb.png)

「さあ、始めましょう！」をクリックします。
![WordPress › 構成ファイルのセットアップ - Google Chrome 2023_08_13 15_37_21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2820434/dc87d0c7-f6ac-8ff9-884d-fe58e8379e5a.png)

データベース名に「wordpress」と入力します。
先ほどコピーしている値を使用して、ユーザー名に`DBUser`の「dbmaster」、パスワードに`DBPassword`の「H&ppyHands0n」、データベースのホスト名に`DBEndpoint`のURLをそれぞれ入力し、「送信」をクリックします。
![WordPress › 構成ファイルのセットアップ - Google Chrome 2023_08_13 15_41_55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2820434/b6da67e4-8657-e62d-2a88-17ca00037265.png)

「インストール実行」をクリックします。
![WordPress › 構成ファイルのセットアップ - Google Chrome 2023_08_13 15_42_17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2820434/ffdb065d-b178-6d95-a937-59b10a0c99fc.png)

サイトのタイトルを「Hello World」、ユーザー名を「admin」、メールアドレスを「admin@example.org」と入力します。
ここでパスワードをコピーしておきます。
「WordPressをインストール」をクリックし、インストールを開始します。
![WordPress › インストール - Google Chrome 2023_08_13 15_43_04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2820434/e3b21aca-a707-3c50-cdeb-a19ed54bd032.png)

インストールが完了したので、WordPressにログインしていきます。
先ほど設定したユーザー名「admin」とコピーしているパスワードを入力し、「ログイン」をクリックします。
![WordPress › インストール - Google Chrome 2023_08_13 15_43_31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2820434/14903346-6727-0206-72ca-d1eca2b58ee2.png)

無事にWordPressにログインできました。
![WordPress › インストール - Google Chrome 2023_08_13 15_43_38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2820434/659ca50b-7229-cefb-a8fa-e6b79cca1ddf.png)

## 8.まとめ、削除
今まで作成したリソースを削除します。
今回は、CloudFormationでリソースを構築したので、スタックを削除することで簡単にリソースを削除することができます。

CloudFormationのコンソールにて、作成した順番と、逆の順序でスタックを削除していきます。

|順番| スタック名 | 
| ---- | ---- |
|1|handson-cfn-elb|
|2|handson-cfn-rds|
|3|handson-cfn-ec2|
|4|handson-cfn|
|5|aws-cloud9-hanson-*|

# 終わりに
一つ一つリソースを削除するのに少し手間がかかっていたのが、今回はスタックを削除して簡単にリソースを削除でき、とても感動しました。。
また、ハンズオンは非常にわかりやすいので、CloudFormationを触ったことがない！という方にもおすすめです！