Clineにインフラ関連の仕事を奪われてしまいました

# はじめに
はじめまして。博報堂テクノロジーズの森田です。パブリッククラウドを扱うインフラエンジニアをしています。最近はClineにハマっていて、あれこれ遊んでみてます。インフラの業務でも使えそうだったので紹介します。

# 今回Clineにやらせたいこと
インフラ手順書の作成って面倒だし退屈ですよね？最近はすっかりIaC化されているものの、やむを得ず人手での対応もあるかと思います。そこで今回はAWSリソースの作成手順書をClineに作ってもらいます。

# Clineの設定
ProviderはGithub Copilot(free)でModelは`claude-3.5-sonnet`を使っています。Custom InstructionsはMemory Bank関連設定と「日本語で応答してください」とだけ入れてます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3717446/3a82b9be-cc66-4b9c-89a0-7495ee5af633.png)

# Clineへの指示文
## 指示テンプレート
指示文をまとめて.clinerulesに定義してみました。
```markdown
#　手順書の書き方
## 文章の特徴
- 読み手はソフトウェアエンジニア・インフラエンジニアが中心です。
- 手順書は再現性が高く、正確性を重要視してください。
- できるだけコピー&ペーストだけで完結するようにしてください。例えばプレースホルダーを多用してしまうと都度対象箇所を修正しながら手順を実行することになり、オペレーションのミスリスクに繋がりますので、変数を使うなどの工夫をしてください。
- 注意すべき点がある場合は補足として記載してください。（例えば、この操作によりサーバーが再起動します、など）
- CLIでの操作を優先してください。GUI操作が必要な場合は具体的にどの順番で操作すればよいかを指示してください。

## 表現
- 基本的に「です・ます」調を使ってください。
- 箇条書きで記載する部分に関しては「です・ます」調を使う必要はなく、体言止めも使用可能です。
- 一つの文章で「です・ます」調とそれ以外の表現が混在するのは避けてください。
```

## 指示１
以下のように指示を出します。
```markdown
VPC作成手順を作成してください
```

この指示で出来上がった手順書がこちらです。
<details>
<summary>vpc-setup.md</summary>

# AWS VPC作成手順

本手順ではAWS CLIを使用してVPCを作成します。

## 前提条件

- AWS CLIがインストールされていること
- AWS認証情報が設定されていること（`aws configure`で設定済み）
- 必要なIAM権限が付与されていること
  - ec2:CreateVpc
  - ec2:ModifyVpcAttribute
  - ec2:CreateInternetGateway
  - ec2:AttachInternetGateway
  - ec2:CreateSubnet
  - ec2:ModifySubnetAttribute
  - ec2:CreateRouteTable
  - ec2:CreateRoute
  - ec2:AssociateRouteTable
  - ec2:CreateTags
  - ec2:DescribeVpcs
  - ec2:DescribeSubnets
  - ec2:DescribeRouteTables
  - ec2:DescribeInternetGateways
  - ec2:DeleteVpc
  - ec2:DeleteSubnet
  - ec2:DeleteRouteTable
  - ec2:DeleteRoute
  - ec2:DeleteInternetGateway
  - ec2:DetachInternetGateway
  - ec2:DisassociateRouteTable

## エラーハンドリング

各コマンドは実行が失敗した場合、エラーメッセージを出力して停止します。エラーが発生した場合は、以下の点を確認してください：

- IAM権限が正しく設定されているか
- 指定したリージョンが正しいか
- CIDRブロックが他のVPCと重複していないか
- リソース名が一意であるか

## 環境変数の設定

以下のコマンドを実行して環境変数を設定します。

```bash
# リージョンの設定
export AWS_REGION="ap-northeast-1"

# VPC名の設定
export VPC_NAME="example-vpc"

# CIDRブロックの設定
export VPC_CIDR="10.0.0.0/16"

# サブネットCIDRの設定
export PUBLIC_SUBNET_1_CIDR="10.0.1.0/24"
export PUBLIC_SUBNET_2_CIDR="10.0.2.0/24"
export PRIVATE_SUBNET_1_CIDR="10.0.11.0/24"
export PRIVATE_SUBNET_2_CIDR="10.0.12.0/24"

# アベイラビリティゾーンの設定
export AZ_1="${AWS_REGION}a"
export AZ_2="${AWS_REGION}c"
```

## VPCの作成

```bash
# VPCの作成
VPC_ID=$(aws ec2 create-vpc \
  --cidr-block $VPC_CIDR \
  --tag-specifications "ResourceType=vpc,Tags=[{Key=Name,Value=$VPC_NAME}]" \
  --query 'Vpc.VpcId' \
  --output text)

echo "Created VPC: $VPC_ID"

# DNS関連の設定を有効化
aws ec2 modify-vpc-attribute \
  --vpc-id $VPC_ID \
  --enable-dns-hostnames '{"Value": true}'

aws ec2 modify-vpc-attribute \
  --vpc-id $VPC_ID \
  --enable-dns-support '{"Value": true}'
```

## インターネットゲートウェイの作成

```bash
# インターネットゲートウェイの作成
IGW_ID=$(aws ec2 create-internet-gateway \
  --tag-specifications "ResourceType=internet-gateway,Tags=[{Key=Name,Value=$VPC_NAME-igw}]" \
  --query 'InternetGateway.InternetGatewayId' \
  --output text)

echo "Created Internet Gateway: $IGW_ID"

# VPCにアタッチ
aws ec2 attach-internet-gateway \
  --vpc-id $VPC_ID \
  --internet-gateway-id $IGW_ID
```

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

```bash
# パブリックサブネット1の作成
PUBLIC_SUBNET_1_ID=$(aws ec2 create-subnet \
  --vpc-id $VPC_ID \
  --cidr-block $PUBLIC_SUBNET_1_CIDR \
  --availability-zone $AZ_1 \
  --tag-specifications "ResourceType=subnet,Tags=[{Key=Name,Value=$VPC_NAME-public-1}]" \
  --query 'Subnet.SubnetId' \
  --output text)

echo "Created Public Subnet 1: $PUBLIC_SUBNET_1_ID"

# パブリックサブネット2の作成
PUBLIC_SUBNET_2_ID=$(aws ec2 create-subnet \
  --vpc-id $VPC_ID \
  --cidr-block $PUBLIC_SUBNET_2_CIDR \
  --availability-zone $AZ_2 \
  --tag-specifications "ResourceType=subnet,Tags=[{Key=Name,Value=$VPC_NAME-public-2}]" \
  --query 'Subnet.SubnetId' \
  --output text)

echo "Created Public Subnet 2: $PUBLIC_SUBNET_2_ID"

# パブリックサブネットの自動パブリックIP割り当てを有効化
aws ec2 modify-subnet-attribute \
  --subnet-id $PUBLIC_SUBNET_1_ID \
  --map-public-ip-on-launch

aws ec2 modify-subnet-attribute \
  --subnet-id $PUBLIC_SUBNET_2_ID \
  --map-public-ip-on-launch
```

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

```bash
# プライベートサブネット1の作成
PRIVATE_SUBNET_1_ID=$(aws ec2 create-subnet \
  --vpc-id $VPC_ID \
  --cidr-block $PRIVATE_SUBNET_1_CIDR \
  --availability-zone $AZ_1 \
  --tag-specifications "ResourceType=subnet,Tags=[{Key=Name,Value=$VPC_NAME-private-1}]" \
  --query 'Subnet.SubnetId' \
  --output text)

echo "Created Private Subnet 1: $PRIVATE_SUBNET_1_ID"

# プライベートサブネット2の作成
PRIVATE_SUBNET_2_ID=$(aws ec2 create-subnet \
  --vpc-id $VPC_ID \
  --cidr-block $PRIVATE_SUBNET_2_CIDR \
  --availability-zone $AZ_2 \
  --tag-specifications "ResourceType=subnet,Tags=[{Key=Name,Value=$VPC_NAME-private-2}]" \
  --query 'Subnet.SubnetId' \
  --output text)

echo "Created Private Subnet 2: $PRIVATE_SUBNET_2_ID"
```

## ルートテーブルの設定

```bash
# パブリックルートテーブルの作成
PUBLIC_RT_ID=$(aws ec2 create-route-table \
  --vpc-id $VPC_ID \
  --tag-specifications "ResourceType=route-table,Tags=[{Key=Name,Value=$VPC_NAME-public-rt}]" \
  --query 'RouteTable.RouteTableId' \
  --output text)

echo "Created Public Route Table: $PUBLIC_RT_ID"

# インターネットゲートウェイへのルートを追加
aws ec2 create-route \
  --route-table-id $PUBLIC_RT_ID \
  --destination-cidr-block 0.0.0.0/0 \
  --gateway-id $IGW_ID

# パブリックサブネットとルートテーブルの関連付け
aws ec2 associate-route-table \
  --subnet-id $PUBLIC_SUBNET_1_ID \
  --route-table-id $PUBLIC_RT_ID

aws ec2 associate-route-table \
  --subnet-id $PUBLIC_SUBNET_2_ID \
  --route-table-id $PUBLIC_RT_ID

# プライベートルートテーブルの作成
PRIVATE_RT_ID=$(aws ec2 create-route-table \
  --vpc-id $VPC_ID \
  --tag-specifications "ResourceType=route-table,Tags=[{Key=Name,Value=$VPC_NAME-private-rt}]" \
  --query 'RouteTable.RouteTableId' \
  --output text)

echo "Created Private Route Table: $PRIVATE_RT_ID"

# プライベートサブネットとルートテーブルの関連付け
aws ec2 associate-route-table \
  --subnet-id $PRIVATE_SUBNET_1_ID \
  --route-table-id $PRIVATE_RT_ID

aws ec2 associate-route-table \
  --subnet-id $PRIVATE_SUBNET_2_ID \
  --route-table-id $PRIVATE_RT_ID
```

## 補足事項

- すべてのコマンドは、設定したAWSプロファイルの権限で実行されます
- 作成されるリソースには、指定したVPC名をプレフィックスとしたタグが付与されます
- パブリックサブネットでは、起動されるインスタンスに自動的にパブリックIPが割り当てられます
- プライベートサブネットからインターネットに接続する場合は、別途NATゲートウェイの設定が必要です
- 本手順で作成されるサブネットは、各アベイラビリティゾーンに2つずつ（パブリック1つ、プライベート1つ）配置されます

## 作成されるリソース一覧

- VPC（CIDR: 10.0.0.0/16）
- インターネットゲートウェイ
- パブリックサブネット2つ（CIDR: 10.0.1.0/24, 10.0.2.0/24）
- プライベートサブネット2つ（CIDR: 10.0.11.0/24, 10.0.12.0/24）
- パブリックルートテーブル（インターネットゲートウェイへのルート含む）
- プライベートルートテーブル

## 確認方法

作成したリソースの一覧を確認するには、以下のコマンドを実行します：

```bash
# VPCの確認
aws ec2 describe-vpcs --vpc-id $VPC_ID

# サブネットの確認
aws ec2 describe-subnets --filters "Name=vpc-id,Values=$VPC_ID"

# ルートテーブルの確認
aws ec2 describe-route-tables --filters "Name=vpc-id,Values=$VPC_ID"
```

## クリーンアップ

作成したリソースを削除する場合は、以下の順序で実行してください：

```bash
# ルートテーブルの関連付けを解除
aws ec2 disassociate-route-table --association-id $(aws ec2 describe-route-tables --route-table-id $PUBLIC_RT_ID --query 'RouteTables[].Associations[].RouteTableAssociationId' --output text)
aws ec2 disassociate-route-table --association-id $(aws ec2 describe-route-tables --route-table-id $PRIVATE_RT_ID --query 'RouteTables[].Associations[].RouteTableAssociationId' --output text)

# ルートテーブルを削除
aws ec2 delete-route-table --route-table-id $PRIVATE_RT_ID
aws ec2 delete-route --route-table-id $PUBLIC_RT_ID --destination-cidr-block 0.0.0.0/0
aws ec2 delete-route-table --route-table-id $PUBLIC_RT_ID

# インターネットゲートウェイをデタッチして削除
aws ec2 detach-internet-gateway --internet-gateway-id $IGW_ID --vpc-id $VPC_ID
aws ec2 delete-internet-gateway --internet-gateway-id $IGW_ID

# サブネットを削除
aws ec2 delete-subnet --subnet-id $PUBLIC_SUBNET_1_ID
aws ec2 delete-subnet --subnet-id $PUBLIC_SUBNET_2_ID
aws ec2 delete-subnet --subnet-id $PRIVATE_SUBNET_1_ID
aws ec2 delete-subnet --subnet-id $PRIVATE_SUBNET_2_ID

# VPCを削除
aws ec2 delete-vpc --vpc-id $VPC_ID
```

注意：クリーンアップを実行する前に、以下の点を確認してください：
- サブネット内にリソース（EC2インスタンスなど）が存在しないこと
- VPC内に追加で作成したリソース（セキュリティグループ、NATゲートウェイなど）が存在しないこと
</details>


## 指示2
次にこの手順書の正確性を確認すべく、手順に従って実際にAWSリソースを操作してもらいます。
まず、最初にClineからAWSリソースを操作できるようプロファイルを設定します。
私の環境ではIdentity Centerを使っているのでログイン画面から一時プロファイル情報をコピーして`~/.aws/credentials`に追加します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3717446/1eec33ac-27b4-4cdd-a956-a2101c68e90d.png)

準備が完了したらClineに指示を出します。前工程で作成されたファイルを`@`付きで指定します。
```markdown
@/manual/vpc-setup.md 
この指示に従いVPCを構築してください。プロファイルは***を使用してください。
構築証跡をファイルとして作成してください。
```
***の部分はIdentity Centerのコピーしたプロファイル名です。[accountId-roleName]形式のものになっているかと思います。

実行が完了すると以下のメッセージが出力されました。実際の環境上にもVPCが作成されていました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3717446/5ace87aa-2b42-4916-9f2e-255435dab0d8.png)

ついでにクリーンアップの手順もやってみます。指示文は以下のようにしてみます。
```markdown
@/manual/vpc-setup.md
この手順に従いクリーンアップしてください。
削除の証跡も残してください。
```
作業が完了すると以下のメッセージが出力されました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3717446/0b08d3b6-04e5-45c5-b2eb-af3e8b78eddf.png)

## おまけ
指示1で作ったものがWell Architected Frameworkに準拠しているかチェックしてもらいます。

```markdown
@/manual/vpc-setup.md 
このファイルが以下のプラクティスに準拠できているかチェックしてください。結果はmarkdown形式でファイル出力してください。
@https://docs.aws.amazon.com/ja_jp/wellarchitected/latest/framework/wellarchitected-framework.pdf
```
<details>
<summary>Well Architected Frameworkレビュー結果</summary>


# AWS VPC作成手順のWell-Architectedレビュー

## 概要
本ドキュメントは、VPC作成手順をAWS Well-Architectedフレームワークの5つの柱に基づいてレビューした結果です。

## セキュリティの柱

### 準拠している項目
- ✅ 最小権限の原則に従ったIAM権限の明確な列挙
- ✅ パブリック/プライベートサブネットの適切な分離
- ✅ サブネットごとの明確なCIDRブロック定義

### 改善推奨事項
- 🔧 VPCフローログの設定手順の追加
- 🔧 セキュリティグループとNACLの設定ガイドラインの追加
- 🔧 暗号化やデータ保護に関する考慮事項の追加

## 信頼性の柱

### 準拠している項目
- ✅ マルチAZ構成の採用（2つのアベイラビリティゾーン）
- ✅ 冗長構成のサブネット設計
- ✅ エラーハンドリングセクションの存在

### 改善推奨事項
- 🔧 障害シナリオと復旧手順の追加
- 🔧 VPCエンドポイントの設定オプションの追加
- 🔧 バックアップと復元戦略の記述

## パフォーマンス効率性の柱

### 準拠している項目
- ✅ 適切なサブネットサイズの設計
- ✅ 効率的なルーティング設定

### 改善推奨事項
- 🔧 パフォーマンスモニタリング方法の追加
- 🔧 トラフィック最適化のベストプラクティス追加
- 🔧 Direct ConnectやVPNの設定オプション追加

## コスト最適化の柱

### 準拠している項目
- ✅ リソースのクリーンアップ手順の提供
- ✅ 必要最小限のリソース構成

### 改善推奨事項
- 🔧 コスト見積もりの追加
- 🔧 コスト最適化のためのタグ戦略の追加
- 🔧 リソース使用量モニタリングの推奨事項追加

## 運用上の優秀性の柱

### 準拠している項目
- ✅ 明確な手順とコマンドの提供
- ✅ リソース作成の確認方法の記載
- ✅ 環境変数を使用した設定の一元管理
- ✅ 補足事項による追加情報の提供

### 改善推奨事項
- 🔧 Infrastructure as Codeの推奨（CloudFormationやTerraform）
- 🔧 運用監視の設定ガイドライン追加
- 🔧 トラブルシューティングガイドの追加

## 総合評価

### 強み
- 基本的なVPCインフラストラクチャの構築手順が明確
- セキュリティとネットワーク分離の基本原則に準拠
- 運用性を考慮した環境変数の活用
- 包括的なクリーンアップ手順の提供

### 改善機会
1. **セキュリティの強化**
   - セキュリティ監視とログ記録の追加
   - アクセス制御の詳細なガイドライン

2. **運用性の向上**
   - Infrastructure as Codeへの移行
   - より詳細なモニタリング戦略

3. **信頼性の向上**
   - 障害復旧手順の詳細化
   - バックアップ戦略の追加

4. **コスト最適化**
   - リソース使用量の最適化ガイドライン
   - コスト追跡と最適化の推奨事項

## アクションアイテム

優先順位順の推奨改善事項：

1. セキュリティ強化
   - VPCフローログの設定追加
   - セキュリティグループとNACLのベースライン設定
   - 暗号化要件の明確化

2. 運用性向上
   - CloudFormationテンプレートの提供
   - モニタリングとアラートの設定
   - 詳細なトラブルシューティングガイド

3. 可用性とディザスタリカバリ
   - フェイルオーバーシナリオの追加
   - バックアップと復元手順
   - 障害対応プレイブック

4. コスト管理
   - リソースタグ付け戦略
   - コスト最適化ガイドライン
   - 使用量モニタリング手順

</details>

概ね良さそうですが、パブリックIPの自動割り当ての指摘とかもしてもらいたかったところなので、調整の余地はありそうです。

## 注意点
設定していて気になった点が2つほどあります。
1. 認証情報を無闇にClineに渡してしまわないように注意が必要
Github上の[プライバシーポリシー](https://github.com/cline/cline/blob/main/docs/PRIVACY.md)を確認する限り、Clineがプロバイダ以外のサーバーに勝手にクライアントの情報や指定したファイルの情報を送ってしまうことはないようです。念の為、うっかり`.env`等のシークレットファイルを送ってしまわないように[`.clineignore`](https://github.com/cline/cline/blob/main/docs/cline-customization/clineignore.md)を定義しておきましょう。プロバイダの選択についても学習されないことを保証してくれるサービスを選定するのも良いかもしれません。Github Copilotの場合、設定から「Allow GitHub to use my data for product improvements」からチェックボックスを外し「Allow GitHub to use my data for AI model training」がDisabledになっていることを確認しておくと良いと思います。
2. 想定外のAWSアカウントを操作してしまわないよう注意
複数のAWSアカウントを扱う場合、確実に想定のAWSアカウントを対象に操作させるように気をつけましょう。プロファイル指定を忘れてうっかりデフォルトのプロファイルで実行されてしまった場合、事故に繋がります。また、AWSベストプラクティスに従って、Clineから使うIdentityに使わせる権限は最小限にしましょう。

## さいごに
実際に動作まで確認ができる手順書の作成がこんなに簡単にできてしまいました。ほんの数ヶ月前までLLMは動かないコマンドを提示してきたりという状態だったのに、しっかりと動くコマンドで最後までやってくれるのは驚きでした。上記の通り一部セキュアでない設定等があったので、手順書作成時にしっかりと人の目で中身をチェックする必要はありそうです。
タイトルは少し大げさですが、本当に仕事を奪われる日が来そうでビクビクしながら仕事をすることになりそうです。