Upgrade to Pro — share decks privately, control downloads, hide ads and more …

カスタム Terraform Provider による運用効率の改善と Terraform P...

Avatar for 110416 110416
November 01, 2025
0

カスタム Terraform Provider による運用効率の改善と Terraform Provider のコード生成ツールの開発

Nikkei Asia のID基盤、マーケティングオートメーションに活用している Piano という SaaSの運用効率化のため Terraform Plugin Framework を利用し、Piano Terraform Provider を開発しました。
この開発のために OpenAPI スキーマから Provider を生成するツールも自作しています。
このスライドでは、その動機・背景と Terraform Plugin Framework を利用した provider 開発の概要について説明します。

Avatar for 110416

110416

November 01, 2025
Tweet

Transcript

  1. Redesigning Nikkei Asia Operation by Custom Terraform Provider & Provider

    Codegen Development Lightning Talk@Nikkei - HashiCorp - TED meetup
  2. WHO AM I Nikkei Asia の全体的な設計の見直し・ アーキテクト・開発を担当しています Software Engineer @

    Nikkei GitHub: https://github.com/i10416 SpeakersDeck: https://speakerdeck.com/i10416
  3. Today's Topic Nikkei Asia で利用する SaaS の管理を効率化するために開発した terraform provider piano

    と terraform provider のボイラープレートを生成する codegen の開発背景を紹介します
  4. Motivation この取り組みのモチベーション・意義は以下の2点です 煩雑な運用の改善という広い範囲の問題を 「terraform provider の開発」 「terraform への リソースの取り込み」 「terraform

    や GitHub を利用した標準的な開発プロセスの導入」 といった具体的で小さな問題群に分割する コード生成を活用することで、その取り組みを限られたリソースでも現実的な時間で実 現可能にする
  5. Example GitHub の PR で plan , マージで apply を実行する典型的なワークフローを想定.

    変更の影 響範囲はテストや plan で特定される. 定義自体は静的なマスターデータに集約し、アプリケーションの環境変数や terraform の file 関数で流し込むことでシステムを跨いだ同期を保証する. # contact flags are user email preferences resource "piano_custom_field" "contact_flags" { for_each = { for record in csvdecode(file("path/to/metadata/contact_flags.csv")): record.name => record } field_name = each.value.name title = each.value.friendly_name data_type = "BOOLEAN" default_sort_order = each.value.sort_order ... }
  6. About Piano Nikkei Asia では piano というサービス を IDaaS・CRM として利用している

    例えば Nikkei Asia のログインフォーム やアカウント管理ページは piano によ って制御されている
  7. Terraform Can Solve the Problems Terraform is an infrastructure as

    code tool that lets you build, change, and version infrastructure safely and efficiently. https://developer.hashicorp.com/terraform/docs Terraform はここで取り上げた問題を解決するのに必要な機能を揃えている バリデーション・テスト レビュー(GitHub の PR) デプロイ自動化 バージョン管理 複数環境の一致
  8. Should We Solve the Problems by Terraform? Why? 答えは "Yes".

    理由は Terraform はインフラや SaaS 管理のデファクトスタンダード 一旦 Terraform 化すれば piano 特有のオペレーションを組む必要がない https://github.com/Nikkei 以下だけでも 133 repositories クラウド、 SaaS や PaaS を跨いでリソースを管理可能 (比較的)低い学習コスト Rule of least power CDK や Pulumi はプログラミング言語のランタイム(TS, Java, etc.)が必要 豊富なドキュメント、ツールチェーンやエコシステム 実現可能性
  9. Terraform Plugin Framework Terraform は HashiCorp Configuration Language(HCL) で記述された構成とステートファイ ルに保存された直近の状態を比較し、差分からリソースの追加・変更・削除処理を呼び出す.

    これはクラウドや SaaS によらない共通の処理. Terraform Plugin Framework はこの内部実装を上手く隠蔽して開発者が Create, Read, Update, Delete など 主要なインターフェースを実装するだけで Terraform Provider を開発で きる ようにしてくれる. また、terraform registry に表示される ドキュメントの自動生成 や テスト・デバッグ用の機 能 なども提供している.
  10. Provider Server Terraform Provider はクライアント(Terraform)からリクエストを受け取ってレスポンスを返す サーバーとして抽象化されている. Terraform の要求するインターフェースを満たすサーバー を実装することで様々なクラウド・SaaS を統一した方法で扱える.

    Terraform providers are server processes that Terraform interacts with to handle each data source and resource operation, such as creating a resource on a remote system. イメージは Language Server と IDE(client) の関係性に近い.
  11. Resource & Datasource Resource はリソースの追加・変更・差分検知・削除など Read・Write 両方の機能の抽象化. resource ブロックで定義される. Datasource

    はデータを取得して terraform 内で利用可能にする Read Only の処理の抽象化. datasource ブロックで定義される.
  12. Model Model は Terraform の state の構造を定義する. Resource と Datasource

    両方で利用される. resource "piano_resource" "foo" { name = "foo" } 上記のブロックは内部的には以下の go の struct に対応する. state ファイルの中を覗くと対応 する JSON object が見つかる. type PianoResourceModel struct { // 開発者が hcl で与える値 Name types.String `tfsdk:"name"` // terraform provider が与える値 Id types.String `tfsdk:"id"` ... }
  13. Schema Schema はモデルのメタデータ(制約やバリデーション)を定義する. フィールドそれぞれにつ いて、必須なのか省略可能なのか、開発者が指定する値なのか provider が与える値なのか、 どのような値を許容するのか、などを指定する. func (r

    *ResourceResource) Schema(...) { resp.Schema = schema.Schema { "rid": schema.StringAttribute { Computed: true, // rid はリソース作成時に自動生成される不変のID }, "name": schema.StringAttribute { Required: true, // name はリソースの作成・更新時に必須なプロパティ ... }, } }
  14. リソース・データソースのライフサイクルに対応する処理 Terraform はリソースやデータソースのライフサイクルに対応するリクエストをサーバー (Provider の実装)に送信する. これに対応するサーバー側の関数のシグニチャは次のパターンに従う. func (recv {Impl Type})

    {Operation}( ctx context.Context, req resource.{Operation}Request, resp *resource.{Operation}Response ) サーバーは受け取ったリクエストを元に外部サービスにリクエストを送り状態を更新しその 結果を Terraform に返す.
  15. Operation は以下のいずれか Configure・Metadata: Resource や Datasource の初期化処理 Read: terraform plan

    や terraform import 時に呼び出される Create: apply 時、state にないリソースブロックが存在すると呼び出される Update: apply 時、既存のリソースの差分が検知されると呼び出される Delete: state に対応するリソースブロックが削除されると呼び出される ImportState: terraform import 時に呼び出される
  16. Problem フレームワークを活用しても書くコードが多い リソース数やオブジェクトのフィールド数に線形に比例するのでスケールしない 追加・更新・削除処理ごとに RPC の型と Terraform の型をマッピングする必要がある enum 型は

    primitive な型にマッピングするかアダプタを実装する必要がある nullable な値は {Type}PointerValue でマッピングする必要がある ネストした配列や構造体もマッピングしなければならない Model と Schema のフィールドを一致させる必要がある
  17. Terraform Provider Codegen Terraform Provider の実装の多くは RPC 向けの型と SDK 向けの型のマッピング・バリデーシ

    ョン. Terraform Provider は基本的な CRUD オペレーションのラッパー ともいえる. また、関 数のシグニチャは処理が違ってもほとんど同じ. つまり OpenAPI specification などの API のメタ情報を利用すれば Provider の処理の多くは 自動生成が可能
  18. 自動生成ツールは次のようなスキーマから "components": { "schemas": { "Sample": { "type": "object", "required":

    ["code"], "properties": { "code": { "type": "integer", "nullable": false }, "message": { "type": "string", "nullable": true }, "details": { "type": "array", "items": { "type": "string" } } } } } }
  19. 次のような実装を生成する(一部抜粋) func (r *SampleDataSource) Read( ctx context.Context, req datasource.ReadRequest, resp

    *datasource.ReadResponse ) { var state SampleDataSourceModel resp.Diagnostics.Append(req.Config.Get(ctx, &state)...) if resp.Diagnostics.HasError() { return } if data.Details != nil { detailsElements := []types.String{} for _, element := range(*data.Details) { detailsElements = append(detailsElements, types.StringValue(element)) } state.Details = detailsElements } state.Message = types.StringPointerValue(data.Message) state.Code = types.Int32Value(int32(data.Code)) resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) }
  20. 実装 典型的なトランスパイラ・コンパイラに寄せた実装(木構造変換器)になっている 1. OpenAPI v3 の JSON から型やメタデータを再帰的に取得・バリデーションする https://github.com/i10416/codegen/tree/main/modules/jsonschema 2.

    JsonSchema を内部表現に持ち上げる https://github.com/i10416/codegen/tree/main/modules/pseudogo 3. 内部表現を Go 言語としてレンダリングする https://github.com/i10416/codegen/tree/main/modules/codegen 余談ですが、コード生成器には機能追加・バグ修正・テストケースの追加など改善の余地が 無数にある
  21. まとめ 運用の改善・効率化という抽象的な問題は Terraform Provider の開発、Terraform へのリ ソースの取り込みと標準的な開発・レビュープロセスの導入という問題に落とし込める 解決策の実現のため Terraform Provider

    を開発した Terraform Plugin Framework を活用することで Custom Provider の開発工数は大幅に削 減できる さらに自作のコード生成器を活用することで Provider の開発を省力化した 余談: コード生成器には改善の余地が無数にあるので PR 歓迎