IaCツールのコードを生成AIで実装する有用性を検討してみる

## 概要

生成AIによるコーディングの効率化がトレンドになっています。

自分もアプリ開発の中でGitHub Copilotを使用しコーディングの効率向上を感じています(この記事自体の作成も、GitHub Copilotの補完をちょくちょく使用しています)。

ITインフラ基盤の構築においても、IaCツールのコードを生成AIで実装することで、IaCツールの開発効率化が期待できるのではないかと考えました。

今回は、例としてAnsible Playbookの単純な処理をGitHub Copilotで実際に実装してみつつ、有用性を検討してみました。

## 想定読者

* 生成AIのIaCツールへの使用用途に興味がある方
* Ansible Playbook + GitHub Copilotの実装に興味がある方

## 目次

前半に今回検証した実装方法を記載しています。後半にはIaCツールで生成AIを使用する際の個人的なベストプラクティスを記載しました。

* 1.Ansible実行環境の準備
* 2.使ってみる1(単純な実装)
* 3.使ってみる2(簡単なロジックを含む実装)
* 4.使ってみる3(テストコードの実装)
* 5.個人的なベストプラクティス
* 6.まとめ

## 1.Ansible実行環境の準備

### 使用したツールと構成

端末上にpython仮想環境を立ち上げ、AnsibleとAnsibleのテスト実施ツールであるMoleculeをインストールします。
また、Ansibleのテスト環境としてDockerを使用することとしたため、Dockerもインストールしました。
各種ツールのインストール方法についてはここでは割愛します。

ざっくりとした構成図は以下の通りです。

![構成図.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2420547/c2336676-c0a3-2c50-5f87-fc040cff6260.png)

* ① molecule initコマンドでroleのディレクトリ構成を作成
* ② molecule testコマンドでテスト環境の作成とplaybookのテストを実施

今回、各ツールのバージョンは以下を使用しました。

| ツール | バージョン |
|:---|:---|
| Ansible | 2.15.2 |
| Molecule | 5.1.0 |
| Docker | 20.10.23 |
| Python | 3.11 |

### GitHub Copilotの使用準備

GitHub Copilotのサブスクリプションを購入し、開発環境上で使用できるようにします。

今回は、端末上のVSCodeにGitHub Copilotプラグインをインストールしました。具体的な使用準備の方法については割愛します。

### Moleculeを使用したAnsible playbookディレクトリ構成の作成

Moleculeでは以下のコマンドを用いてroleを作成します。

```bash
$ molecule init role <namespace名>.<role名> -driver-name docker
```

上記コマンド実施により、以下のディレクトリ構成が作成されます。

```bash
$ tree
.
├── README.md
├── defaults
│   └── main.yml
├── files
├── handlers
│   └── main.yml
├── meta
│   └── main.yml
├── molecule
│   └── default
|       ├── converge.yml
│       ├── molecule.yml
│       └── verify.yml
├── tasks
│   └── main.yml
├── templates
└── vars
    └── main.yml
```

基本的にはAnsible Galaxyのinitで作成されるroleと同様のディレクトリ構成となっていますが、テスト時に構築されるテスト環境の設定やテストコードを実装するためのmoleculeディレクトリが追加で存在します。

今回は簡単のためdocker上でテスト用のコンテナを構築する設定を使用しています(initコマンド実施時にdriver-nameオプションにdockerを指定することで設定可能です)。また、molecule/verify.ymlにテストコードを実装します。

他はAnsible Galaxyと構成は一緒の認識です。以降の実装についてはtasks/main.ymlに実装しました。

## 2.使ってみる1(単純な実装)

molecule initコマンドで`create_user`roleを作成しました。

linux環境上でグループを作成し、ユーザを作成するというタスクをmain.ymlに実装します。この際、GitHub Copilotのサジェスト機能を使用して実装してみました。

グループを作成する、ユーザを作成する旨コメントアウトを記載すると、それぞれ以下サジェストされました。

![create_user_suggest1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2420547/56888964-4abc-8043-0c69-399f83ea9204.png)

![create_user_suggest2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2420547/fecf8364-6d14-4942-e5f9-e466dedffe22.png)

生成されたコードに間違いはないですが、例えばuserのパスワードを指定するといった設定を追加するなどの余地はあります。サジェストされたコード以外により適切なモジュールがないか、設定値の追加が必要かなどは確認が必要かと思います。

添付画像では表現されていませんが、testグループの作成をするコードがサジェストされた後、testユーザーを作成するというコメントアウトもサジェストされました。このように、設定すべき内容自体もサジェストされるのも生成AIの強みだと思います。ユーザを作成するというタスクについては、改善余地はあるもののコードを書かずに実装ができました。

Ansible playbookの実装テストのため、create_userディレクトリ上で以下コマンドを実行します。

```bash
$ molecule test
```

少し脱線しますが、molecule testコマンドを実行すると、ざっくり以下のシナリオが実行されます。作成したplaybookの冪等性、事前に作成したテストの実行が1コマンドで可能です。

* molecule create
  * テスト用の環境(今回はコンテナ)が構築される。
* molecule converge
  * テスト用の環境に対して、テスト対象のroleが実行される。
  * convergeは2回実行され、冪等性の確認ができる。
* molecule verify
  * テスト用の環境に対して、テストコードが実行される。molecule/verify.yml内のtaskが実行される。
* molecule destroy
  * テスト用の環境が破棄される。

create_userのroleに対しmolecule testコマンドを実行し、テスト環境上でユーザが作成されたことが確認できました。 以下Converge部分の抜粋です。

```
PLAY [Converge] ****************************************************************

TASK [Gathering Facts] *********************************************************
ok: [instance]

TASK [Include acme.create_user] ************************************************

TASK [acme.create_user : create test group] ************************************
changed: [instance]

TASK [acme.create_user : create test user] *************************************
[WARNING]: 'append' is set, but no 'groups' are specified. Use 'groups' for
appending new groups.This will change to an error in Ansible 2.14.
changed: [instance]

PLAY RECAP *********************************************************************
instance                   : ok=3    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
```

## 3.使ってみる2(簡単なロジックを含む実装)

molecule initコマンドで`test_logic`roleを作成しました。

ある変数がtrueだったらディレクトリを作成し、handlerを実施するといった簡単なロジックの実装を行います。tasks/main.ymlとhandlers/main.ymlに実装します。この際、GitHub Copilotのサジェスト機能を使用して実装してみました。

まずはtasks/main.ymlにロジックの内容を記載すると、以下サジェストされました。

![test_logic_suggest1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2420547/9e96a1b3-b672-943a-fb0d-f9ec54ad2172.png)

次にhandlers/main.ymlにロジックの内容を記載すると、以下サジェストされました。

![test_logic_suggest2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2420547/81f30633-9d79-e9df-e8aa-2a4d31ce0716.png)

それぞれ特に難しい実装ではないため、問題ない内容がサジェストされました。一方で、コード実装者がhandlerの実装箇所を把握していないと実装ができないという点があります。今回は難しい実装ではないのでさほど問題はありませんが、ファイル間をまたぐような実装はサジェストされたコードをそのまま使用するのは難しいかもしれません。

こちらもmolecule testコマンドでテストを実施し、期待通りの設定がされたことを確認できました。

## 4.使ってみる3(テストコードの実装)

前節で作成したtest_logicのテストコードを実装します。molecule initコマンドで作成されたmolecule/verify.ymlにテストコードを記載します。この際、GitHub Copilotのサジェスト機能を使用して実装してみました。

GitHub Copilotの場合、編集中のファイルだけでなく、開いている別タブのファイル情報も参照してコード提案に影響を与えているようです。tasks/main.ymlを開いた状態でそのタスクの設定が期待通りであるかテストするタスクを、molecule/verify.yml上で実装してみました。

8行目のコメントアウトを記載した後、サジェストにより以下のようなコードとなりました。

![test_suggest1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2420547/cda5ea68-9d41-e01b-1189-56037b514777.png)

テストコードの実装についてもコードを書かず実装ができました。

また、今回は簡単な実装だったため恩恵はあまり感じられないかもしれませんが、テストケースの機械的な網羅は人間よりも生成AIの方が正確なはずなので、テストコードの実装にはその観点でも有用だと思います。

## 5.個人的なベストプラクティス

Ansible Playbookの実装にGitHub Copilotを使用するメリットは以下3点があると個人的には思っています。

* role内のタスクの実装を補完してくれる
* role内で設定すべき内容をサジェストしてくれる
* roleのユニットテストの実装を補完してくれる

一方で、以下のような使用方法は効果は低いと思っています。

* roleより大きい単位の構造のサジェスト
  * 例えば、今回検証内で使用していませんが、実際にAnsibleを使用する際は、Ansible実行先のホストの構成をinventoryファイル等で管理すると思います。これらの構成の検討はシステム構成にも強く依存するため、生成AIでサジェストした内容を使用するというよりかはシステム構成に沿ったplaybook構成の検討を人間がすべきだと思います。
* ファイル間の依存関係の検討
  * 生成AIでサジェストされる内容は、基本的にはファイル内のタスクの実装に関するものです。handlerやtemplateなど、ファイル間の依存関係は、生成AIでサジェストされうるかもしれませんが誤生成のリスクは高まるため、まずは人間が検討し、それぞれのファイル内の実装について生成AIを使用するのが良いと思います。

上記を踏まえ、Ansible Playbookの実装時にGitHub Copilotを使用する際のベストプラクティスを検討しました。

* [step0]コード実装者のスキルセット
  * 生成AIを最大限効率的に使用するためには、以下のスキルセットが必要かと思います。
    * 実装したい内容を簡潔かつ正確にコメントアウトで記載できる
    * 生成AIでサジェストされた内容が適切かどうかを判断できる
    * handler,templateなどの構成要素を理解しており、それらの実装をどのファイルに実装するかを判断できる
* [step1]playbook全体の構造を検討する
  * ディレクトリ構造の検討
    * コード自体は生成AIで実装できますが、playbook全体の構造は生成AIでは実装できません。そのため、playbook全体の構造を検討する必要があります。
    * Ex. Ansible実行先ホストの設定方法の検討
      * Ansible実行先ホストの設定にinventoryファイルの作成や各ホスト固有の変数設定方法を検討する必要があります。こちらについてベストな方法はシステムによって異なるはずで、こちらをいちいち生成AIで実装するのは効率が悪く、人間が検討すべき観点だと考えます。
* [step2]roleのplaybookを格納するディレクトリ構成を体系化する
  * playbookを参照するのは人間なので、第三者が見ても理解しやすい構造にする必要があります。
  * 体系的な構造を検討する、またはAnsible Galaxyや今回使用したMoleculeによる初期構造を作成するなどの工夫が考えられます。
* [step3]各roleのplaybookとそれに紐づくテストコードを作成する
  * role内のtaskは生成AIの力を借りるのが効果的かと思います。一方で品質が担保できているか見落とすリスクが高くなるため、以下の工夫が必要だと考えます。
    * コード実装者が生成AIのサジェストを適切に判断する
      * step0に記載した通り、サジェストされた内容が適切かどうかの判断は必要です。
    * 期待された設定ができるか・冪等性は担保されているかをテストする
      * Ansibleモジュール自体のテストは必要ないとは思うものの、期待された変数値を使用しているか、適切な実装により冪等性が担保されているかはテストが必要かと思います。
      * 実装のたびに上記を手動で確認するのは大変なので、今回使用したmoleculeなどのテストツールを使用するのが効果的です。
      * テストコードの作成にも生成AIの使用が効果的です。テストケースの網羅にも使用できます。

実際にはstep3を繰り返し実施し、適宜step1とstep2を見直すという流れで実装を進めるのが良いと考えました。

これはあくまでもコード実装に限った話であり、複数人で開発する場合のルールやCIによる自動化などは別途考慮する必要はあると思います。

## 6.まとめ

今回はAnsibleを例に検討をしましたが、他のIaCツールでも同様のことが言えると思っています。個人的に生成AIの特徴は以下だと考えており、それを踏まえた上で使用することが重要だと思います。

* 生成AIを使用することで、ユニット単位のコード実装の効率化が期待できる
* 一方でコードの品質が知らぬ間に低下するリスクがあるため、以下を満たしていることを前提に使用することが望ましい
  * コード実装者は以下のスキルを持っていること
    * 実装したい内容を簡潔かつ正確にコメントアウトで記載できる
      * 生成AIの誤生成のリスクを減らすため
    * ユニットよりも大きな単位の構造を検討できている
      * ファイルをまたがるような実装は生成AIで実装できない、実装できたとしても誤生成のリスクはより高くなる。
      * 生成AIで実装できる箇所をできる限り増やせるよう構造を事前に検討できると良い。
  * 実装したコードのテストが実施できる環境があること。
    * 実装したコードの品質を担保するため
    * テストに時間がかかると生成AIでコード実装を効率化した恩恵が得られない。テストツールを使用するなどの工夫を事前に検討しておくと良い。
    * テストケースの網羅・テストコードの実装にも生成AIを使用できると効率的

最後に、今回のベストプラクティスの整理はあくまでも個人的な意見であり、以下の観点で改善可能箇所が存在しうる点ご理解いただければと思います。
* 複数人で開発する・大規模なシステムを構築する際、上記のベストプラクティスが適用できない可能性がある
* 生成AIの機能が向上することで、上記のベストプラクティスが変わる可能性がある

今後も使用感を確かめ、ベストプラクティスの改善を行っていきたいと思います。