「Console-to-Code」でVPC＋EC2＋RDSの基本構成をクリック操作だけでコード化してみる！

## はじめに
最近、AWSから正式リリースされた「AWS Console-to-Code」という機能を見つけました。普段から、インフラ構築をコード化するIaC（Infrastructure as Code）を書く手間を少しでも減らしたいと考えていたため、この機能は今後役立つのではないかと思い、試してみることにしました。





## AWS Console-to-Codeについて 

AWS Console-to-Codeとは、マネジメントコンソール上での操作を自動でコード（AWS CLI コマンド、CloudFormation テンプレート、AWS CDK コードなど）に変換してくれるサービスです。手動操作からインフラのコード管理（IaC）への移行が簡単になり、効率的にコード化できます。


個人的には、AWS CLIによるリソース操作コードを自動生成できるため、手順書を作る手間が省くことができ、とても助かりそうです。


#### 現在、対応しているサービスは以下の3つです：

・Amazon EC2
・Amazon RDS
・Amazon VPC

#### 対応しているコード形式
・AWS CloudFormationテンプレート（YAMLまたはJSON形式）
・AWS CDK（TypeScript、Python、Javaで出力可能）

#### 詳細はこちらから

https://aws.amazon.com/jp/about-aws/whats-new/2024/10/general-availability-console-to-code-generate-code/

https://aws.amazon.com/jp/blogs/news/convert-aws-console-actions-to-reusable-code-with-aws-console-to-code-now-generally-available/



## AWS Console-to-Codeを試してみる

##### 構成要件
今回は、以下のVPC、EC2、RDSまでの基本的な構成を手動で作成し、その過程をConsol-to-Codeで記録してスタック化を試してみました。

![スクリーンショット 2024-11-04 0.50.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3925805/f255f73d-34d3-1e4a-8fd2-1691fa7d0b39.png)



##### AWS Console-to-Code の始め方

Console-to-Code を使用するために、まずEC2サービスのコンソールに移動します（VPC や RDS のコンソールからも利用可能です）

1. 画面右側に表示される「Console-to-Code」ウィジェットをクリックします。
2. 「記録を開始」ボタンをクリックすると、操作の記録が開始されます。

![画像2.tiff](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3925805/e6913b51-bb9d-3cd0-5070-5ceeb9cc54b6.tiff)


##### AWS Console-to-Codeの見方
**・記録されたアクション**
　コンソール上で実行した操作内容をもとに、情報を収集してオペレーションを表示してくれます。

　**・タイプ**
　　操作内容は、「書き込み」と「読み取り」の二つのタイプに分類されます。

　　・書き込み：インスタンスの作成や設定のアタッチなど、データが変更される操作。
　　・読み取り：画面遷移時に読み込まれる情報の取得など、データに変更がない操作。
![修正1のコピー.tiff](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3925805/ba6d90ce-749a-b6ad-3dfe-f66f691a695f.tiff)




##### VPC、EC2、RDS の作成
要件どおりに VPC、EC2、RDSをすべて作成して、最終的には以下のように記録されました。
「停止」ボタンをクリックすることで、記録を終了します。

![修正2のコピー.tiff](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3925805/d2715302-c971-ea17-0891-5adbfec34360.tiff)




記録が完了すると、「記録されたアクション」欄で タイプ を「書き込み」に設定し、必要なアクションにチェックを入れます。今回は CloudFormation の YAML を生成しようと思います。
![画像4のコピー.tiff](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3925805/ba540353-afe0-b217-1fbd-937a62435be3.tiff)
※ちなみに、タイプが「読み取り」の場合、CLI コマンドはコピーできますが、YAML などのコードは生成できなかったです。


##### YAML コードの生成
実際に YAML コードが生成され、ダウンロードできるようになりました。
![修正３のコピー.tiff](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3925805/a2d5b1e9-9b32-0cd0-f7ac-5a4fd26bc13b.tiff)



生成したyamlのコード↓
```ruby:console-to-code.yaml
Resources:
  VPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: 10.0.0.0/16
      InstanceTenancy: default
      EnableDnsHostnames: true
      EnableDnsSupport: true
      Tags:
        - Key: Name
          Value: test-dev-vpc

  PublicSubnetA:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      AvailabilityZone: !Select [3, !Split ['-', !Ref 'AWS::Region']]
      CidrBlock: 10.0.0.0/24
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: public Subnet A

  PrivateSubnetA:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      AvailabilityZone: !Select [3, !Split ['-', !Ref 'AWS::Region']]
      CidrBlock: 10.0.20.0/24
      MapPublicIpOnLaunch: false
      Tags:
        - Key: Name
          Value: Private Subnet A

  PrivateSubnetB:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      AvailabilityZone: !Select [3, !Split ['-', !Ref 'AWS::Region']]
      CidrBlock: 10.0.30.0/24
      MapPublicIpOnLaunch: false
      Tags:
        - Key: Name
          Value: Private Subnet B
        
  InternetGateway:
    Type: AWS::EC2::InternetGateway

  VPCGatewayAttachment:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      VpcId: !Ref VPC
      InternetGatewayId: !Ref InternetGateway

  PublicRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC

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

  PublicSubnetARouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PublicSubnetA
      RouteTableId: !Ref PublicRouteTable

  PublicSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupName: test-dev-public-sg
      GroupDescription: test-dev-public-sg
      VpcId: !Ref VPC
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 22
          ToPort: 22
          CidrIp: xxxx

  PrivateSecurityGroup:  
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupName: test-dev-private-sg
      GroupDescription: test-dev-private-sg
      VpcId: !Ref VPC
      SecurityGroupIngress:
        - IpProtocol: tcp 
          FromPort: 3306
          ToPort: 3306
          SourceSecurityGroupId: !Ref PublicSecurityGroup

  EC2Instance:
    Type: AWS::EC2::Instance
    Properties:
      ImageId: ami-xxx
      InstanceType: t2.micro
      NetworkInterfaces:
        - AssociatePublicIpAddress: "false"
          DeviceIndex: "0"
          GroupSet: 
            - !Ref PublicSecurityGroup
          SubnetId: !Ref PublicSubnetA
      CreditSpecification:
        CPUCredits: standard
      Tags:
        - Key: Name
          Value: test-dev-EC2

  DBSubnetGroup:
    Type: AWS::RDS::DBSubnetGroup
    Properties:
      DBSubnetGroupDescription: test-dev-RDS-sub
      SubnetIds: 
        - !Ref PrivateSubnetA
        - !Ref PrivateSubnetB

  RDSInstance:
    Type: AWS::RDS::DBInstance
    Properties:
      Engine: mysql
      EngineVersion: 8.0.39
      DBInstanceClass: db.t3.micro
      DBSubnetGroupName: !Ref DBSubnetGroup
      VPCSecurityGroups: 
        - !Ref PrivateSecurityGroup
      AllocatedStorage: 20
      BackupRetentionPeriod: 1
      DBName: ""
      MasterUsername: admin
      MasterUserPassword: test1234
      PreferredBackupWindow: 02:00-03:00
      PreferredMaintenanceWindow: sun:05:00-sun:06:00
      PubliclyAccessible: false
```
初心者目線ではありますが、生成されたコードを確認して、特に印象に残ったポイントは以下の通りです。

・VPC、サブネット、インターネットゲートウェイ、ルートテーブル、セキュリティグループ、EC2インスタンス、RDSインスタンスが設定どおりに連携し、正確に定義されている点は良かったです。
・一方で、RDSのパスワードがコード内に平文で記載されていたため、AWS Secrets ManagerやLambdaなどを使って機密情報を保護する必要があると感じました。


##### CloudFormation でのスタック作成

最後に、生成されたYAMLコードをそのまま使ってCloudFormationでスタックを作成しようとしたところ、`Template error: Fn::Select cannot select nonexistent value at index 3`というエラーが発生しました。
![スクリーンショット 2024-11-04 000450.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3925805/1bd18a62-ad48-83fb-6400-73b297555390.png)

調べたところ、問題はコード内で指定したアベイラビリティゾーン（AZ）のインデックスにありました。`AvailabilityZone: !Select [3, !Split ['-', !Ref 'AWS::Region']]`という記述がエラーの原因でした。

このコードはAZリストのインデックス`3`を指定していますが、東京リージョンではAZが3つ（インデックス`0〜2`）しかないため、存在しないAZを参照してしまい、エラーが発生していました。


![スクリーンショット 2024-11-04 141341.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3925805/bf7e59f4-f1e2-4f88-1960-6bdfe0673b94.png)


エラーを解消するため、以下のようにAZリストから最初の要素（インデックス`0`）を選択するコード`AvailabilityZone: !Select [0, !GetAZs '']`に修正しました。
![スクリーンショット 2024-11-04 172228.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3925805/632fa70a-bd1d-a587-dde2-ca6ee80e0e8b.png)







修正後、CloudFormationでスタックを再作成したところ、正常に作成が完了しました！

![画像5のコピー.tiff](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3925805/bb9f9a7a-7bf0-a387-e101-c21657002c0c.tiff)



## まとめ
今回は、AWS Console-to-Code を使用して VPC、EC2、RDS の基本構成をコード化し、CloudFormation でデプロイする手順を試してみました。
自動生成されたコードには一部修正が必要でしたが、大部分を自動でコード化できるのは、凄いと感じました。今後、他のサービスでも Console-to-Code が利用できることを期待しています！