diagramsコードから構成通りのIaCをChatGPTに書いてもらう

Terraformの勉強がてら、Pythonパッケージ diagrams のコードを基に IaC コードを生成AIに書いてもらおうという取り組みです。

## 作成したい環境

```python
from diagrams import Diagram, Cluster
from diagrams.aws.compute import EC2
from diagrams.aws.storage import S3
from diagrams.onprem.client import Client
from diagrams.aws.network import Endpoint

with Diagram("Simple Diagram") as diag:
    with Cluster('VPC'):
        with Cluster('Public subnet'):
            ec2_pub = EC2('Public Instance')
        with Cluster('Private subnet'):
            ec2_pri_A = EC2('Private Instance A')
            ec2_pri_B = EC2('Private Instance B')
        eS3 = Endpoint('S3 Endpoint (Gateway)')

    Client() >> ec2_pub
    ec2_pub - ec2_pri_A
    ec2_pub - ec2_pri_B
    ec2_pri_A - eS3
    ec2_pri_B - eS3
    eS3 - S3('')
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3777293/afec03b2-d0a6-ae6c-cd26-967ab2a1b39e.png)

シンプルだし何を目的としているかいまいちわからない構成ですが、こちらでやってみます。

## ChatGPTにお願いしてみる

### 入力1
以下のコードで描かれるAWS構成図を作成するためのIaCコードを書いてください。
```
from diagrams import Diagram, Cluster
from diagrams.aws.compute import EC2
from diagrams.aws.storage import S3
from diagrams.onprem.client import Client
from diagrams.aws.network import Endpoint, PrivateSubnet, PublicSubnet

with Diagram("Simple Diagram") as diag:
    with Cluster('VPC'):
        with Cluster('Public subnet'):
            ec2_pub = EC2('Public Instance')
        with Cluster('Private subnet'):
            ec2_pri_A = EC2('Private Instance A')
            ec2_pri_B = EC2('Private Instance B')
        eS3 = Endpoint('S3 Endpoint (Gateway)')

    Client() >> ec2_pub
    ec2_pub - ec2_pri_A
    ec2_pub - ec2_pri_B
    ec2_pri_A - eS3
    ec2_pri_B - eS3
    eS3 - S3()
```

### 出力1
> 以下は、指定されたIaCコードに基づいてAWS構成図を作成するためのTerraformコードの例です。

```terraform
provider "aws" {
  region = "us-west-2" # 任意のリージョンを選択してください
}

resource "aws_vpc" "main" {
  cidr_block = "10.0.0.0/16"
}

resource "aws_subnet" "public" {
  vpc_id            = aws_vpc.main.id
  cidr_block        = "10.0.1.0/24"
  availability_zone = "us-west-2a" # 任意のアベイラビリティーゾーンを選択してください
}

resource "aws_subnet" "private" {
  vpc_id            = aws_vpc.main.id
  cidr_block        = "10.0.2.0/24"
  availability_zone = "us-west-2a" # 任意のアベイラビリティーゾーンを選択してください
}

resource "aws_instance" "public_instance" {
  ami           = "ami-12345678" # EC2インスタンス用のAMIを指定してください
  instance_type = "t2.micro"
  subnet_id     = aws_subnet.public.id
}

resource "aws_instance" "private_instance_a" {
  ami           = "ami-12345678" # EC2インスタンス用のAMIを指定してください
  instance_type = "t2.micro"
  subnet_id     = aws_subnet.private.id
}

resource "aws_instance" "private_instance_b" {
  ami           = "ami-12345678" # EC2インスタンス用のAMIを指定してください
  instance_type = "t2.micro"
  subnet_id     = aws_subnet.private.id
}

resource "aws_s3_bucket" "example" {
  bucket_prefix = "example"
}

resource "aws_vpc_endpoint" "s3_endpoint" {
  vpc_id               = aws_vpc.main.id
  service_name         = "s3"
  vpc_endpoint_type    = "Gateway"
  subnet_ids           = [aws_subnet.public.id]
}
```

> このTerraformコードでは、AWSのVPC、サブネット、EC2インスタンス、S3バケット、VPCエンドポイントなどのリソースが定義されています。リソースのIDやAMIなど、適切な値に置き換えてください。

指定はしなかったですが、Terraformで書いてくれましたね。
リージョンとAMIを指定しなかったので変更してもらいます。

### 入力2
EC2のAMIは "ami-0ab3794db9457b60a"、リージョンは "ap-northeast-1" に変更してください。

### 出力2
> 了解です。指定されたAMIとリージョンに変更したTerraformコードを以下に示します。

```terraform
provider "aws" {
  region = "ap-northeast-1"
}

resource "aws_vpc" "main" {
  cidr_block = "10.0.0.0/16"
}

resource "aws_subnet" "public" {
  vpc_id            = aws_vpc.main.id
  cidr_block        = "10.0.1.0/24"
  availability_zone = "ap-northeast-1a"
}

resource "aws_subnet" "private" {
  vpc_id            = aws_vpc.main.id
  cidr_block        = "10.0.2.0/24"
  availability_zone = "ap-northeast-1a"
}

resource "aws_instance" "public_instance" {
  ami           = "ami-0ab3794db9457b60a"
  instance_type = "t2.micro"
  subnet_id     = aws_subnet.public.id
}

resource "aws_instance" "private_instance_a" {
  ami           = "ami-0ab3794db9457b60a"
  instance_type = "t2.micro"
  subnet_id     = aws_subnet.private.id
}

resource "aws_instance" "private_instance_b" {
  ami           = "ami-0ab3794db9457b60a"
  instance_type = "t2.micro"
  subnet_id     = aws_subnet.private.id
}

resource "aws_s3_bucket" "example" {
  bucket_prefix = "example"
}

resource "aws_vpc_endpoint" "s3_endpoint" {
  vpc_id               = aws_vpc.main.id
  service_name         = "s3"
  vpc_endpoint_type    = "Gateway"
  subnet_ids           = [aws_subnet.public.id]
}
```

> このコードでは、指定されたAMIとリージョンが `ami-0ab3794db9457b60a` と `ap-northeast-1` に設定されています。

問題なく変更されているようです。
今回はあまり気にすることでもないですが、リソース名に接頭辞を付けるよう指示してみます。

### 入力3
すべてのリソース名に接頭辞 "diag-test-" をつけてください。

### 出力3
> 理解しました。すべてのリソース名に接頭辞 "diag-test-" を追加したTerraformコードを以下に示します。

```terraform
provider "aws" {
  region = "ap-northeast-1"
}

resource "aws_vpc" "main" {
  cidr_block = "10.0.0.0/16"
}

resource "aws_subnet" "public" {
  vpc_id            = aws_vpc.main.id
  cidr_block        = "10.0.1.0/24"
  availability_zone = "ap-northeast-1a"
}

resource "aws_subnet" "private" {
  vpc_id            = aws_vpc.main.id
  cidr_block        = "10.0.2.0/24"
  availability_zone = "ap-northeast-1a"
}

resource "aws_instance" "diag-test-public_instance" {
  ami           = "ami-0ab3794db9457b60a"
  instance_type = "t2.micro"
  subnet_id     = aws_subnet.public.id
}

resource "aws_instance" "diag-test-private_instance_a" {
  ami           = "ami-0ab3794db9457b60a"
  instance_type = "t2.micro"
  subnet_id     = aws_subnet.private.id
}

resource "aws_instance" "diag-test-private_instance_b" {
  ami           = "ami-0ab3794db9457b60a"
  instance_type = "t2.micro"
  subnet_id     = aws_subnet.private.id
}

resource "aws_s3_bucket" "diag-test-example" {
  bucket_prefix = "example"
}

resource "aws_vpc_endpoint" "diag-test-s3_endpoint" {
  vpc_id               = aws_vpc.main.id
  service_name         = "s3"
  vpc_endpoint_type    = "Gateway"
  subnet_ids           = [aws_subnet.public.id]
}
```

> 上記のコードでは、すべてのリソース名に "diag-test-" 接頭辞が付与されています。

ぱっと見VPCのリソース名に接頭辞ついてないですが、まあいいでしょう。
インスタンスタイプも t2.micro にしてくれていますし、こちらを実際に構築してみます。

## 環境構築
書いてもらった Terraform コードを実際に走らせてみます。
環境は Cloud9 上に構築します。

構築手順はこちらの記事など参照。

https://zenn.dev/akeru/articles/e7d719f49f65c7

(昔はデフォルトでTerraform入っていたようですが、今は手動で入れるっぽいですね)

自分は Amazon Linux 2023 上に Cloud9 を立て、 Terraform v1.8.2 をインストールしています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3777293/1cf61198-949a-3fc5-5f85-50d39edcb37d.png)

test.tf というファイルに ChatGPTが作成したコードを貼り付けて保存します。

ここから terraform コマンドを実行していきます。
```
$ terraform init
```
こちらは問題なく `Terraform has been successfully initialized!` が返ってきました。

続いて、

```
$ terraform validate
```
こちらも問題なく `Success! The configuration is valid.` が返ってきました。流石に構文ミスとかはないんですかね。

ではいよいよ、
```
$ terraform apply
```
ここでじっくりパラメータ等を確認すればいいのですが、今回は問答無用でyesを入力します。

と、ここでエラー発生。
```
╷
│ Error: creating EC2 VPC Endpoint (s3): InvalidParameter: Subnet IDs are only supported for Interface and GatewayLoadBalancer type VPC Endpoints.
│       status code: 400, request id: db1694b9-b2b7-415a-b6d3-a724cf1d69da
│ 
│   with aws_vpc_endpoint.diag-test-s3_endpoint,
│   on test.tf line 43, in resource "aws_vpc_endpoint" "diag-test-s3_endpoint":
│   43: resource "aws_vpc_endpoint" "diag-test-s3_endpoint" {
│ 
╵
```
Gateway型のエンドポイントなのに subnet_ids を指定してるのが問題のようですね。
さらに言うとサービス名が "S3" となっていますが、"com.amazonaws.ap-northeast-1.s3" と指定しなおす必要があります。
ま、ここまで来ると手で直した方が早いですね。。。

Endpointのところを以下のように修正して再度 apply します。
 ```terraform
 resource "aws_vpc_endpoint" "diag-test-s3_endpoint" {
  vpc_id               = aws_vpc.main.id
  service_name         = "com.amazonaws.ap-northeast-1.s3"
  vpc_endpoint_type    = "Gateway"
  route_table_ids = [aws_vpc.main.main_route_table_id]
}
```
無事に `Apply complete!` しました。

## コンソールから確認
作成したリソースをコンソールからもざっくり確認してみましょう。
VPCまわりは良さそう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3777293/a359a231-9c0c-086d-e198-3187cace2541.png)

バケット名はこんな感じになるんですね...知らなかった。消そう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3777293/68e80422-b821-1068-42b9-2212e107531c.png)

インスタンスもprivate 2台、public 1台作成されていますね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3777293/4c6efd28-2310-a895-2686-c802274e8840.png)

## まとめ
diagrams の Python コードからまぁまぁなTerraformコードを作成できました。
一発で思い通りにいかなくてもラリーを繰り返すうちに要求通りのものができていきそうです (最終的には自分で手直しする方が早いでしょうけど)。
これ以上複雑な構成になるとどうなることやらですが、、、

そもそもdiagramsのコード用意するのがめんどいとか構成図には描かないようなパラメータの方が重要なんじゃとか色々課題はありますが、基盤構築がもっと楽になるといいですね。
個人的には Terraform ほとんど使ったことがなかったので勉強になりました。