ゼロから始める OSS 貢献:Terraform の AWS Provider に新規リソースを追加するための 11 のステップ

こんにちは。 DBRE チーム所属の @p2sk です。
DBRE(Database Reliability Engineering)チームでは、横断組織としてデータベースに関する課題解決やプラットフォーム開発に取り組んでいます。
先日、OSS のリポジトリ terraform-provider-aws に対してコントリビュートする機会がありました。具体的には、AWSが 2024 年 10 月に一般提供を開始した DynamoDB or S3 と Redshift との マネージドなデータ連携を Terraform で管理できるようにする「aws_redshift_integration」というリソースを新規実装しました。PR は既にマージされており、v5.95.0でリリースされ、機能が使えるようになりました。
私は今回が初めての OSS 貢献だったので、完走できるか不安な部分もありましたが、生成 AI の力も借りながら PR の作成までやり切ることができました。
新機能が AWS で GA されてから、Terraform で管理できるようになるまで(=terraform-provider-aws に新規リソースとして実装されるまで)、数ヶ月かかる場合もあります。そのような場合に、実装されるのを待つ代わりに自分で実装する選択肢を取れるようになったことは、大きなアドバンテージだと感じました。そこで本記事では、私と同じように初めて Terraform の AWS Provider に新規リソースを追加するコントリビューションに取り組みたいと考えている方に向けて、初回から効率的に取り組んでもらえるような情報を提供することを目指します。
いずれはコーディングエージェントに issue を渡すだけで PR 作成まで行ってくれる未来になっているかもしれませんが、現時点ではそこまでは難しい印象で、同じような境遇の方の参考になれば幸いです。
今回追加したリソースについて
今回は以下の 2 つの機能を管理できるリソースを追加しました。それぞれ簡単に説明します。
- DynamoDB から Redshift への zero-ETL 統合
- S3 から Redshift への event integration
zero-ETL 統合は、ETL パイプラインの構築が不要な、マネージドなデータ連携機能です。「zero-ETL 統合」という機能は、最初は Aurora MySQL と Redshift 間のデータ連携として提供が開始され、その後複数のデータソースとターゲットに対象が拡大しています。アーキテクチャ図を以下に示します。
出展:AWS - Amazon Redshift との Amazon DynamoDB ゼロ ETL 統合の始め方
S3 から Redshift への event integration も同様に、S3 バケットに追加されたファイルを自動かつ高速に Redshift に連携する機能です。
これらは機能としては別々ですが、Integration を作成する API は同一です。terraform-provider-aws のリソースは API にマップされるため、この API を Terraform 対応させることで、上記 2 つの機能が同時に実装されることになります。したがって、追加したリソース自体は 1 つだけです。
リソース追加の判断基準
公式ドキュメントに以下の記載があります。
New resources are required when AWS adds a new service, or adds new features within an existing service which would require a new resource to manage in Terraform. Typically anything with a new set of CRUD API endpoints is a great candidate for a new resource.
和訳:
AWS が新しいサービスを追加したり、既存のサービスに新しい機能を追加したりする場合、Terraform で管理するための新しいリソースが必要になります。一般的に、新しい CRUD API エンドポイントセットを持つものは、新しいリソースの候補として最適です。
ということで、新しい CRUD API エンドポイントセットを持つかどうか
が大きな判断基準となりそうです。今回はこれを満たしており、新規リソース追加の実装を行いました。
Contribution の流れ
流れは公式のドキュメントに非常によくまとまっています。
- 開発環境を準備
- コードのデバッグ(今回は新規リソース追加のため、スキップ)
- コードを変更
- テストを書く
- 継続的インテグレーション
- 変更ログを更新
- プルリクエストを作成
上記の項目をベースにしつつ、本記事で推奨する手順を以下に整理します。また、各項目でかかる労力を個人の実体験をベースに「★」の数で示します。詳細な手順は公式のドキュメントの確認が必要ですが、実際に取り組んで得られた知見をまとめているため、ぜひ参考にしてください。
- 関連 issue の調査または作成 ★
- 対応する AWS API と SDK の事前調査 ★
- 開発環境を準備 ★
- 対象リソースの動作検証・依存リソースのコード化 ★★★
- scaffolding ツールでベースとなるコードを生成 ★
- コード修正と動作確認 ★★★★★
- テストを書く ★★★
- 継続的インテグレーション用のテストのローカル実行 ★★
- ドキュメント修正 ★
- プルリクエストを作成 ★
- 変更ログを作成して push ★
以降は各手順の詳細について触れていきますが、開発をスタートする前に知っておくと良さそうなことを先にまとめます。
留意事項
複数種類の SDK による書き方が混在
terraform-provider-aws では、リポジトリ内で異なる SDK を用いた 2 パターンの書き方が混在しています。
- Terraform plugin framework
- 現時点で使用が推奨されている、新しい SDK
- Terraform Plugin SDKv2
- 旧 SDK のため新規利用は非推奨。ただし、この SDK で実装されたリソースのバグ修正などで使うこともある
- サポート終了の v1 でのコードもまだ存在している可能性もあるため、正確には 3 パターンかも
従って、生成 AI に調査やコーディングを依頼する場合は Terraform plugin framework を対象にすることをプロンプトに含める方が良いです。
この辺りの歴史的経緯などに興味のある方は、ChatGPT Deep Research のリサーチ結果を、ハルシネーション混入の可能性に留意いただきつつご確認ください。
ライセンスについて
Terraform 本体は 2023年にライセンスが BSL に変更され、OSS の定義からは外れていますが、terraform-provider-aws は引き続き MPL 2.0 で、OSS のままになっています。
各種プロバイダは Terraform から fork されたopentofu でも使われているようです。opentofu 用の AWS Provider も terraform-provider-aws から fork されているため、プロバイダーにコントリビュートすると、間接的に Terraform と opentofu 両方に貢献できることになりそうです。
この辺りの経緯に興味のある方は ChatGPT Deep Research のリサーチ結果をご覧ください。(ハルシネーションに関して同様にご留意ください)
以降では、実際の手順について解説します。なお、記事内でのテストの実行時間などについては、以下の環境におけるおおまかな体感の値です。
- マシン : MacBook Pro
- チップ : Apple M2 Pro
- メモリ : 32 GB
1. 関連 issue の調査または作成
PR の作成時は、関連 issue へのリンクを記載します(例:下図「Relations」)。そのため、関連する issue を最初に探して、なければ作成します。
既に issue が作成されている場合は、他の誰かが取り組んでいるケースもあります。issue のコメントを一読し、「まだ誰も着手していなさそうか」などは確認しておくと良さそうです。
今回は既に issue が作成されていたため、PR 作成時は既存 issue へのリンクを記載しました。
2. 対応する AWS API と SDK の事前調査
リソースを新規実装するためには、go の SDK(aws-sdk-go-v2)が該当リソースの CRUD 操作に対応している必要があります。基本的には GA されたタイミングで SDK も提供されると推測しますが、多少のラグはあるかもしれません。terraform-provider-aws の go.mod も、該当リソースに対応したバージョンにアップデートされている必要がありますが、メンテナーにより頻繁に更新されている印象なので、自分でアップデートしなくても、メンテナーにより最新化されている可能性が高い、という認識で良さそうです。
今回は、以下のリファレンスをブックマークに登録しておくことで、開発途中に都度参照できて便利でした。生成 AI に参照させる場合も重宝すると思います。
API リファレンス
- https://docs.aws.amazon.com/ja_jp/redshift/latest/APIReference/API_CreateIntegration.html
- https://docs.aws.amazon.com/ja_jp/redshift/latest/APIReference/API_ModifyIntegration.html
- https://docs.aws.amazon.com/ja_jp/redshift/latest/APIReference/API_DeleteIntegration.html
- https://docs.aws.amazon.com/ja_jp/redshift/latest/APIReference/API_DescribeIntegrations.html
SDK リファレンス
- https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/redshift#Client.CreateIntegration
- https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/redshift#Client.ModifyIntegration
- https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/redshift#Client.DeleteIntegration
- https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/redshift#Client.DescribeIntegrations
当初は DynamoDB の zero-ETL 統合を Terraform 対応させるのがモチベーションだったのですが、リファレンスを見ると API の SourceARN パラメータのパターンが下図の通り S3 にも対応していることがわかり、S3 の integration も同時に検証しなければいけないことに気づけました。このように、検証のスコープが想定より広い場合もあるため、リファレンスの入出力は最初に一通り確認しておくと良さそうです。
出展:AWS - Redshift CreateIntegration API Reference
また、リソースの種類によっては Delete が存在しないものや、Modify が存在しない場合もあるかもしれませんので、その場合は提供されている API だけ実装すれば OK です。例えば、Aurora MySQL と Redshift 間の zero-ETL 統合は、GA 時点では Create / Delete / Describe のみ提供され、後で Modify が提供されたようです。
Redshift は SDK のディレクトリとして redshift と redshiftserverless が存在し、片方でいいのか両方実装すべきなのか迷いましたが、redshiftserverless 側には該当の API が存在しなかったのと、redshift 側の関数で serverless 用の Integration も作成できたことから、redshift 側だけ実装すれば良いと判断しました。
3. 開発環境を準備
公式のドキュメント に記載の手順通りに実行すれば OK です。ただし、実際のリソースを作成して動作確認を行う make testacc
はこの時点では不要です。make test
も実行しなくても良いかもしれませんが、私の環境では 3-40 分ほどかかりました。
Using the Provider に記載の手順を実行することで、手元で build した Provider を使って Terraform コマンドを実行できるようになります。実行時に以下のような warning が表示されることを確認できれば OK です。
これで、自分で build した Provider が Terraform 実行時に使われていることが確認できます。なお、後述の「受け入れテスト」というテスト経由でも動作確認自体はできるのですが、build と動作確認のサイクルを高速に回すためには、自分で build した Provider を使って Terraform コマンドを実行するのが良いと思います。個人的には、この手順で動作確認することで、普段と同じ Terraform の使い方の中で動作が確認できてイメージしやすかったです。詳細にデバッグしたい場合は delve を使うと良さそうです。
4. 対象リソースの動作検証・依存リソースのコード化
コーディングを開始する前に、まずは追加しようとしている新規 AWS リソースの動作確認を手元で行っておくと理解が深まります。この時、大抵は新規の AWS リソースを作成する前に、依存しているリソースを作成する必要があると思います。例えば、今回は以下の AWS リソースに依存しています。(正確には、Source は プロビジョニングされた Redshift / Redshift Serverless / S3 のいずれかだけで OK)
- aws_vpc
- aws_subnet
- aws_security_group
- aws_dynamodb_table
- aws_dynamodb_resource_policy
- aws_redshift_subnet_group
- aws_redshift_parameter_group
- aws_redshift_cluster
- aws_redshift_resource_policy
- aws_kms_key
- aws_kms_alias
- aws_kms_key_policy
- aws_redshiftserverless_namespace
- aws_redshiftserverless_workgroup
- aws_s3_bucket
- aws_s3_bucket_public_access_block
- aws_s3_bucket_policy
- aws_iam_role
- aws_iam_role_policy
- aws_redshift_cluster_iam_roles
このような依存リソースは、この時点で tf ファイルとしてコード化しておくことを強くお勧めします。理由は以下の通りです。
- 検証、開発が 1 日で終わらない場合はコストがかかるため、都度 apply & destroy したい
- 後述の「受け入れテスト」でほぼ同様の Configuration を用意する必要があるので、最初に用意しておくと後で楽
terraform fmt
でフォーマットもしておくと後述の CI テストのローカル実行もより楽に
生成 AI を活用することで HCL のコーディングはかなり時短できると思います。依存リソースのコード化が完了したら、あとはコンソールか AWS CLI を使って手動で対象リソースを作成して動作検証します。
5. scaffolding ツールでベースとなるコードを生成
新しいリソースを追加する場合は、Skaff という scaffolding ツールを使ってベースとなるコードを生成することが推奨されています。
リソースタイプ名は命名規則が決まっており、aws_${サービス名}_${AWSリソース名}
となります。AWS リソース名は、sdk の関数名に従う必要があります。例えば今回は「CreateIntegration」関数が提供されているため、AWS リソース名は「Integration」となります。サービス名はリポジトリの service ディレクトリの値を使えば良さそうです。
したがって、今回のリソースタイプ名は aws_redshift_integration
となります。この名前はブランチ名にも反映する必要があり、f-aws_redshift_integration
としました。skaff では AWS リソース名を指定すれば良いので、該当サービスのディレクトリへ移動した後に、以下のコマンドを実行しました。
$ pwd
/Users/masaki.hirose/workspace/terraform-provider-aws/internal/service/redshift
$ skaff resource --name Integration
Skaff を実行することで、リソース用のコード、テストコード、ドキュメントの 3 ファイルが生成されるようです。生成されたファイルはこちらからご確認いただけますが、コメントが手厚く記載された親切なファイルになっています。また、これらの初期状態のファイル達と、最終的にマージされたコードを比較すると、どこを修正すべきかイメージがつきやすいと思います。
6. コード修正と動作確認
生成されたコードをベースに、動作するように修正していきます。ドキュメントに記載の通り、Resource Schema をまずはコーディングして、その次に CRUD ハンドラを実装していきます。
Terraform plugin framework では、CRUD ハンドラがそれぞれ「Create」「Read」「Update」「Delete」という直感的な関数名になっています。例えば、初めて terraform apply
して新規リソースが作られる際に、ここで実装した Create() 関数が呼ばれます。その中で go の SDK の対応する関数(この場合は CreateIntegration)が実行され、内部的には対応する AWS API(この場合は CreateIntegration)が実行され、リソースが作成される、という流れになります。
terraform apply
で Replace を伴わない修正のみが実行される場合は Update() 関数が、terraform destroy
でリソースを削除する場合は Delete() 関数が実行されます。リソース情報を読み取る必要がある場面では都度 Read() 関数が呼ばれます。
Resource Schema の実装
Schema() 関数の中で、Terraform が受け付ける引数(arguments)および出力する属性(attributes)をスキーマ情報として定義します。以下のコードのように、Attributes マップに各フィールドを定義します。各属性はキーが Terraform 上の名前(snake case)、値が schema.Attribute インタフェースを実装した構造体で、schema.MapAttribute や schema.StringAttribute などから適切なものを用います。
// 修正後の Schema() 関数の一部を抜粋
func (r *integrationResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
resp.Schema = schema.Schema{
Attributes: map[string]schema.Attribute{
"additional_encryption_context": schema.MapAttribute{
CustomType: fwtypes.MapOfStringType,
ElementType: types.StringType,
Optional: true,
PlanModifiers: []planmodifier.Map{
mapplanmodifier.RequiresReplace(),
},
},
names.AttrARN: framework.ARNAttributeComputedOnly(),
names.AttrDescription: schema.StringAttribute{
Optional: true,
},
"integration_name": schema.StringAttribute{
Required: true,
},
上記のように、SDK のリファレンスで必須になっているパラメータには Required: true
を指定し、変更時にリソース置き換えが必要な場合は RequiresReplace()
modifier を付与します。適切な modifier を使い分けるのが個人的には難しかったです。modifier は自分でも実装でき、1 つだけどうしても自前で実装が必要と判断して実装したのですが、PR 作成後にメンテナーによって既存の modifier に置き換えられていました。迷った場合は stringplanmodifier など、対象の型に対応する modifier で提供されている関数を最初に理解して、使えないか確認するのが良いと思います。PR 作成後のメンテナーからのフィードバックを通して、基本的には既存の modifier でカバーできるのだなという学びがありました。
合わせて、ResourceModel 構造体も定義します。
type integrationResourceModel struct {
AdditionalEncryptionContext fwtypes.MapValueOf[types.String] `tfsdk:"additional_encryption_context"`
Description types.String `tfsdk:"description"`
IntegrationARN types.String `tfsdk:"arn"`
IntegrationName types.String `tfsdk:"integration_name"`
KMSKeyID types.String `tfsdk:"kms_key_id"`
SourceARN fwtypes.ARN `tfsdk:"source_arn"`
Tags tftags.Map `tfsdk:"tags"`
TagsAll tftags.Map `tfsdk:"tags_all"`
TargetARN fwtypes.ARN `tfsdk:"target_arn"`
Timeouts timeouts.Value `tfsdk:"timeouts"`
}
CRUD ハンドラと関連処理の実装
CRUD ハンドラはいずれも、SDK への入力構造体を組み立てて、SDK の関数を呼び出す流れで実装します。また、CRUD ハンドラ内で利用する関数も合わせて実装します。これには、以下のようなものがあります。
- 該当リソースの情報を取得するための finder 関数
- リソースの作成、修正、削除が完了するまで待つ waiter 関数
- リソースのステータスを取得する status 関数
- リソースを全削除する sweeper 関数(テスト向けの機能であり、必須ではない模様)
注意点として、service によっては wait.go / find.go など専用の go ファイルが用意されており、その場合はそのファイルに処理を追記する必要があります。そうでない場合は、実装中のファイルに全て処理を記述して問題ないようです。今回対象となった redshift サービスでは wait.go などが存在しており、そちらに追記する形をとりました。
リソースの登録
実装が完了したあとは、Terraform Provider にそのリソースを認識させる登録処理を行います。以下のようなアノテーションが必要ですが、skaff で生成されたコードにすでに入っているので自分で記述する必要はありません。削除しないようにだけ注意しておけば OK です。
// @FrameworkResource("aws_redshift_integration", name="Integration")
func newIntegrationResource(context.Context)
上記アノテーションを記述したら、プロジェクトルートディレクトリで make gen
を実行します。これにより、各サービスパッケージ内の service_package_gen.go が再生成され、新規実装したリソースも Provider 登録されます。ここまできたら、make build
でビルドし、通れば実際に terraform apply
などで動作を確認できる状態になります。
動作確認
新規実装したリソースを HCL で記述し、terraform apply
して動作確認します。手順「# 4. 対象リソースの動作検証・依存リソースのコード化」において、すでに依存リソースはコード化が終わっているので、ここでは新規実装したリソースだけを、別のディレクトリの新規ファイルに記述し、別の state で管理します。これにより、動作確認したい新規リソースだけを apply & destroy できるので確認スピードが上がります。もしくは以下のようにターゲットを指定することで、1 ファイルに全て記載している場合でも個別に apply する方法を取るのでもいいと思います。
terraform plan -target=new_resource_type.hoge -out=myplan
terraform apply myplan
7. テストを書く
terraform-provider-aws では、以下の 3 つのテストがあります。
- 受け入れテスト (Acceptance Tests)
- Terraform を実行し、AWS 上にリソースを作成したり、更新、削除できることを確認するテスト。実際にリソースを操作するので金銭的費用が発生。そのため「実行は任意」とドキュメントに書かれている。
- ユニットテスト
- 関数レベルのテスト。今回は不要と判断し、未作成。
- CI テスト
- PR 作成後に実行される、lint / format などを含む包括的なテスト。
CI テストはすでに用意されているものを実行するだけなので、受け入れテストとユニットテストが、コントリビューターが書くべきテストになります。ユニットテストは複雑な処理などを実装した場合に書くことが推奨されており、今回は不要と判断し、受け入れテストだけを書きました。受け入れテストでは、以下のコードのように、テストに必要な AWS リソースを HCL で記述する必要があります。
func testAccIntegrationConfig_base(rName string) string {
return acctest.ConfigCompose(acctest.ConfigVPCWithSubnets(rName, 3), fmt.Sprintf(`
data "aws_caller_identity" "current" {}
data "aws_partition" "current" {}
resource "aws_security_group" "test" {
name = %[1]q
vpc_id = aws_vpc.test.id
ingress {
protocol = -1
self = true
from_port = 0
to_port = 0
}
...
ステップ「4. 対象リソースの動作検証・依存リソースのコード化」で既にコード化が完了していると、ここはほぼコピペで行けるので非常に楽でした。テストを実行する場合は、以下のように関数名を指定すると関数単位で実行できます。
make testacc TESTS=TestAccRedshiftIntegration_basic PKG=redshift
リソース単位で一気にテストするには、以下のように「_」以降を削除して実行します。
make testacc TESTS=TestAccRedshiftIntegration PKG=redshift
8. 継続的インテグレーション用のテストのローカル実行
terraform-provider-aws リポジトリは、コードの品質を確保するための厳格な CI パイプラインを持っています。PR 作成後に実行されるものですが、ローカルでこれらのチェックを実行できる機能が提供されているので、できる限りローカル上でパスするように修正しておくと親切かと思います。
完全なチェックは make ci
で実行できますが、私の環境だと数時間かかったので、まずは make ci-quick
で検出された問題を修正してから make ci
を実行するのが、待ち時間を最小化できると思います。
私の場合は何度か修正を行い、ローカル環境で make ci-quick
を実行した際に全てのチェックをパスできましたが、make ci
実行時は一箇所だけ GNUmakefile を修正しないとエラーになる箇所がありました。これは自分の環境特有の問題かもしれないため、PR に含めずローカルだけの修正で回避しました。
ドキュメントに記載がありますが、make testacc-lint-fix
を最初に実行すると、terrafmt
関連の問題だけは自動で修正してくれるようなので、最初に実行しておくのも一手かと思います。
9. ドキュメント修正
skaff で生成されたドキュメントを修正します。ここで書いた内容が、よく見るドキュメントとして反映されます。既存のドキュメントを参考に書いていけば問題ないと思います。
10. プルリクエストを作成
こちらは特に迷うことはないと思います。
11. 変更ログを作成して push
公式のドキュメントに沿って問題なく作成できると思います。ファイルの命名規則的に PR の番号が必要なので、まず PR を出して、その後に変更ログを作成して push すれば OK です。
ここまでで、PR を作成するまでの流れをお伝えしました。以降では、本取り組みにより得た知見をシェアします。
メンテナーによる修正内容
先日、PR は無事にマージされ、v5.95.0でリリースされ、機能が使えるようになりました。マージまでに、メンテナーによりコードが修正されていたので、どのような修正が行われたかをご紹介します。
schema.Attribute から ID の削除
skaff で生成されたコードにも以下のコメントが記述されていましたが、気づけずに ID 属性を残していたところ、不要とのことで削除されていました。AWS API のリファレンスを見て残すか決めた方が良さそうです。
// Only include an "id" attribute if the AWS API has an "Id" field, such as "IntegrationId"
names.AttrID: framework.IDAttribute(),
変数名などの変更
この修正がもっとも多く、自分の命名規則の確認が甘かったと思われます。一方で、「resourceIntegrationModel」という構造体名は skaff によって自動で生成されたものですが、「integrationResourceModel」という修正が加えられており、この辺りは skaff による命名規則の実装に若干の不備があるのかもしれません。
自作の modifier を既存の modifier へ置き換え
ある問題を解決するために、自分で plan modifier を実装したのですが、既存の modifier に修正されていました。
ここは自分でも自信がない箇所だったので、手厚めに PR にコメントを残しておいたところ、以下のコメントもいただき、既存の modifier をもっとよく調べておくべきだったと反省しました。しかし、「なぜこの実装にしたのか」を詳細に示しておいたことで、メンテナーが修正の判断を行いやすい状態にはできたかなと思います。
This can be accomplished with the RequiresReplaceIfConfigured plan modifier.
この修正は LLM の力を借りて導き出せなかったかを確認するために、実装中に使っていたプロンプトを修正して LLM に渡したところ、上記の既存 modifier での修正案を提案してくれました。開発時は modifier を自作するしかないと判断して LLM を使っていたためプロンプトの指示が具体的になりすぎていたのが良くなかったようです。LLM の使い方の改善余地もあるという学びを得ました。
受け入れテスト時のチェック項目の追加
こちらのコミットにあるように、受け入れテスト時は、そのテストシナリオによって「リソースの作成」を期待するのか、「リソースの更新」を期待するのかまで記述できることを知りました。これにより、意図しないリソースの再作成が発生した場合に気づける、といった効果が期待できそうです。
AWS リソース作成にかかったコスト
今回は受け入れテストも自分で実行し、かつ個別に動作検証も行ったため、AWS リソースの作成による金銭的コストが発生しました。Terraform で IaC 化して、使わないときはこまめに destroy していましたが、合計で約 50$ ほどかかりました。大半は Redshift のコストであり、ここは実装しようとするリソース次第で大きく変わってきそうです。
その他の所感
学び:標準化のために膨大なエネルギーが注がれている
Terraform 関連のような、数千人規模のコントリビューターが関わっているリポジトリでは、「誰が走っても同じゴールにたどり着くためのレール」の整備が欠かせません。標準化が甘いと、メンテナー(レビュワー)の負担が大きくなり、機能のリリーススピードが落ちてしまいます。そのような背景から、以下のような様々な情報やツールの提供によってコードの標準化を強く推進する意思を感じました。
- 豊富なドキュメントの提供
- 貢献種別(バグ修正、新規リソース追加など)ごとの詳細な手順の整理
- 命名規則などのルールの記載
- 専用ツール「skaff」による scaffolding
- 「これを修正していけばOK」なベースとなるコードを自動生成
- ローカルで実行可能な CI テスト
- lint / format / test など、様々な観点で徹底的にチェック可能
- ローカルでパスしておけば、PR 時の CI も全てパスする可能性が高く、メンテナーの負担が下がる
特に、CI テストと同等なものをローカルで実行できるようにするためにエネルギーを注いでいるのは、下記の通りドキュメントからも伝わってきます。
NOTE: We've made a great effort to ensure that tests running on GitHub have a close-as-possible equivalent in the Makefile.
和訳
注: GitHub で実行されるテストについては、Makefile に可能な限り同等のコードが含まれるよう最大限の努力を払っています。
これにより、例えば const で定義している定数については、以下のように値をハードコーディングしていると定数を使うように促されるなど、細かい点までコード内の記述の揺らぎが最小化される力が働きます。
このように、テスト項目は非常に細かく多岐に渡りますが、逆にいうと、受け入れテストとローカルでの CI テストをパスした状態であれば、初めての PR であっても自信を持って作成できました。
私が所属する DBRE チームでは、DevOps のスペシャリストにより上記のような scaffolding から format, lint, test までの開発フローを一通り整理してくれていたため、今回の開発の流れにも違和感なく取り組むことができ感謝しています。
反省点:生成 AI の活用方法に改善の余地あり
初見のリポジトリに対する理解速度を上げるために、Copilot などでリポジトリをインデックス化しておけば、コード理解がより速くなったと反省しています。一方で、今回のように異なる SDK によるコードが混在するリポジトリの場合は、前提として現在推奨されている SDK を指定した上で質問するなどの工夫も必要だと感じました。実際、Plan Modifier 周辺を Deep Research で調べて Web 上の Issue に書かれていた解決策を試したものの、旧 SDK での解決方法だったため、動作しませんでした。代わりに、関連ソース一式を LLM に渡したところ、ほぼ修正不要のコードが提案されて問題を解決できました。LLM をもっとうまく活用してキャッチアップや開発速度を上げていきたいです。
苦労した点:異なる種類の SDK によるコードの混在
前述の通り、異なる種類の SDK によるコードが混在しているため、「既存コードが全て参考になるわけではない」という状況で、これに気づくのに少し時間がかかりました。例えば、sweeper 関数の実装では、現在の SDK(Terraform plugin framework) と 以前の SDK とで実装方法が異なります。今回の実装対象サービスは redshift ですが、redshift の sweeper 関数を実装するファイルではまだ現在の SDK での実装が行われておらず、旧 SDK での実装を参考にしてコードを書いたけど動かない、といった問題に遭遇しました。現在の SDK で実装されている関数を別サービスから探して参照することで解決しましたが、参考にする既存コードが現在の推奨 SDK による記法かどうかは気をつけると良さそうです。
手順別の AI と人力の使いわけ
最後に、各手順を AI と人力どちらで行った方がいいのか、主観ですが現時点での見解を表にまとめます。今回の開発完了後に、検証目的で同じタスクを AI エンジニアの Devin にも実施させてみましたが、公式ドキュメントにも書いてある通り、細かくタスクを分解して渡す必要がありそうでした。もちろん、現時点での見解なので、AI の進化によって変化していくと思われます。
手順 | AI / 人力 | 備考 |
---|---|---|
1. 関連 issue の調査または作成 | 人力 | Web検索または GitHub Issues ページを自分で調べるのが最速 |
2. 対応する AWS API と SDK の事前調査 | 人力 | 自分で検索するのが早い |
3. 開発環境を準備 | 人力 | 自分でセットアップした方が早い |
4. 対象リソースの動作検証・依存リソースのコード化 | AI+人力 | 依存リソースのコード化は LLM 活用が有効 |
5. scaffoldingツールでベースとなるコードを生成 | 人力 | 自分で実行した方が早い |
6. コード修正と動作確認 | AI+人力 | ベースを LLM に書かせ、細部は自分で仕上げる |
7. テストを書く | AI+人力 | ベースを LLM に書かせ、細部は自分で仕上げる |
8. CI用テストのローカル実行 | AI or 人力 | AI に任せると自動でパスするようにコードを修正してくれるかもしれないが、テスト実行時間が長いので製品によってはクレジット消費が大きくなる懸念 |
9. ドキュメント修正 | AI+人力 | マージ済みドキュメントを参考として渡して LLM に下書きを作らせると良さそう |
10. プルリクエストを作成 | 人力 | 自分で行うのが早そう |
11. 変更ログを作成して push | 人力 | 自分で行うのが早そう |
まとめ
Terraform Provider へのコントリビューションは敷居が高く感じていましたが、ガイドがしっかりと整備されており、scaffolding ツールやテストフレームワークが充実しているので、慣れればスムーズに進められると感じました。初回だったのでドキュメントの読み込みに時間がかかりましたが、次回からはより高速に開発を進められると思います。AWS の新機能リリースをいち早く Terraform 化したいという方は、ぜひ取り組んでみてください。その際に、本記事が少しでも参考になれば幸いです。
KINTO テクノロジーズ DBRE チームでは一緒に働いてくれる仲間を絶賛募集中です!カジュアルな面談も歓迎ですので、 少しでも興味を持っていただけた方はお気軽に X の DM 等でご連絡ください。併せて、弊社の採用 Twitter もよろしければフォローお願いします!
関連記事 | Related Posts
We are hiring!
【クラウドエンジニア】Cloud Infrastructure G/東京・大阪
KINTO Tech BlogWantedlyストーリーCloud InfrastructureグループについてAWSを主としたクラウドインフラの設計、構築、運用を主に担当しています。
【プラットフォームデベロッパー】プラットフォームG/東京・大阪
プラットフォームグループについてAWS を中心とするインフラ上で稼働するアプリケーション運用改善のサポートを担当しています。