こんにちは!インフラエンジニアの皆さん、日々のインフラ管理にお疲れ様です。
Terraform、AWS CDK、そしてPulumi……。インフラをコードで管理する(IaC)手法は、現代の開発現場においてなくてはならないものになりました。
特にPulumiは、TypeScript、Python、Goといった使い慣れた汎用プログラミング言語を使ってインフラを定義できるため、条件分岐やループ、関数化といったプログラミングの恩恵をフルに受けられるのが魅力ですよね。
さて、ここで一つ、現場でよくある頭痛の種についてお話しさせてください。
「AWSやKubernetesは綺麗にIaC化できたけど、うちの社内システム(オンプレのレガシーDB、社内独自REST API、あるいは自社製クラウド基盤)はどうするんだ……?」という問題です。
手動でポチポチ設定したり、泥臭いシェルスクリプトやAnsibleのプレイブックで無理やり収束させたりしていませんか?
もしあなたが「社内独自のシステムも、他のAWSリソースと一緒にPulumiで美しく、宣言的に管理したい」と感じているなら、今回の記事はまさにドンピシャです。
今回は、Pulumiの真骨頂である「カスタムプロバイダ開発(Bridge技術とSchema駆動開発)」の世界へあなたを招待します。これをマスターすれば、社内のどんなブラックボックスなAPIも、あなたの手でモダンなIaCリソースに生まれ変わらせることができますよ。さあ、一緒に扉を開けてみましょう!
—
1. なぜ「カスタムプロバイダ」が必要なのか?
Pulumiは、背後に「プロバイダ」と呼ばれるプラグインを抱えることで、AWS、GCP、Azure、Kubernetesなど、数千を超えるリソースを操作しています。プロバイダの正体は、Pulumiエンジンからの指示を受け取り、実際のAPIを叩いてリソースの作成・更新・削除を行う gRPC サーバーです。
もし、世の中に存在しない「社内製API」や「自社開発のマイクロサービス基盤」をPulumiで操作したい場合、どうすればいいでしょうか?
答えはシンプルで、「自分たちのカスタムプロバイダを作ればいい」のです。
カスタムプロバイダを作るアプローチには、主に以下の2つがあります。
1. Terraform Provider Bridge: すでにTerraformのプロバイダが存在する場合、それを自動的にPulumiのプロバイダに「ブリッジ(変換)」する手法。(一番楽でパワフル)
2. Pulumi Schema駆動開発: ゼロから独自にgRPCサーバーを実装するか、PulumiのSchema定義からコードを自動生成する手法。(完全オリジナルのAPI向け)
今回は、多くの現場で即効性がある 「Terraform Provider Bridge」 を使った実用的なアプローチをベースに、その仕組みと魅力を優しく解説していきます。
—
2. ブリッジ(Bridge)技術の全体像:どうやって動いているのか?
「Terraformの資産をPulumiで使い回せたら最高なのに……」
実は、Pulumi社はその夢を叶えるための強力なツールチェインを用意しています。それが `pulumi-tf-provider-boilerplate` や `pf` (Plugin Framework) をベースにしたブリッジツールです。
[ あなたの書いたPulumiコード (TS / Python / Go) ]
↓ (Pulumi Engine)
[ ブリッジされたカスタムプロバイダ (gRPC) ]
↓ (Terraform Providerのロジックを内包)
[ 社内独自REST API / レガシーシステム ]
内部的には、Terraformのプロバイダが持つ「リソースのスキーマ定義」と「APIコールバック(CRUD処理)」を、Pulumiが理解できる形に翻訳(ブリッジ)しています。これにより、Terraformプロバイダが持つ堅牢なAPI連携の歴史と実績を、そのままPulumiのエコシステムに持ち込むことができるのです。
—
3. 実践:社内APIを管理するカスタムプロバイダを作ってみよう
ここからは、手を動かしながら具体的な手順を見ていきましょう。
今回は例として、社内に存在する「ユーザー管理REST API(`https://api.internal.net/v1/users`)」をPulumiで宣言的に管理するためのプロバイダを構築するシナリオを想定します。
ステップ 1: 開発環境の準備
まずは、プロバイダ開発に必要なツールをインストールします。Go言語がプロバイダ開発の標準言語となるため、Go環境が必要です。
Goのインストール確認(1.21以上推奨)
go version
Pulumi CLIのインストール確認
pulumi version
ステップ 2: ブリッジプロジェクトの雛形作成
Pulumi公式が提供しているボイラープレート(ひな形)をクローンして、独自のプロバイダプロジェクトを作成します。
ボイラープレートのリポジトリをクローン(または新規作成)
git clone https://github.com/pulumi/pulumi-tf-provider-boilerplate.git pulumi-internal-user-provider
cd pulumi-internal-user-provider
モジュール名の変更(ご自身の環境に合わせて書き換えてください)
go mod edit -mod github.com/your-org/pulumi-internal-user/v3
このボイラープレートの中には、TerraformのプロバイダSDKをラップし、Pulumi用のgRPCスキーマを出力するための魔法のボイラープレートコードがすでに詰まっています。
ステップ 3: プロバイダのスキーマとリソース定義
プロバイダの心臓部となる `provider/resources.go` を編集します。ここで、どのTerraformリソースをPulumiの世界にマッピングするかを定義します。
// provider/resources.go のイメージ
package internaluser
import (
“path/filepath”
“github.com/pulumi/pulumi-terraform-bridge/v3/pkg/tfbridge”
shimv2 “github.com/pulumi/pulumi-terraform-bridge/v3/pkg/tfshim/sdk-v2”
// 自社製Terraformプロバイダのインポート(または内部実装)
internal_tf “github.com/your-org/terraform-provider-internal-user/provider”
)
// Provider は Pulumi プロバイダのメタデータを返します
func Provider() tfbridge.ProviderInfo {
prov := tfbridge.ProviderInfo{
P: shimv2.NewProvider(internal_tf.New(“v1.0.0”)()),
Name: “internaluser”,
// どの言語(TS, Pythonなど)向けにSDKを自動生成するかを指定
CSharp: &tfbridge.CSharpInfo{},
Python: &tfbridge.PythonInfo{},
Golang: &tfbridge.GolangInfo{},
NodeJS: &tfbridge.NodeJSInfo{},
}
// 個別リソースのマッピング定義
prov.SetAutonaming(255, “-“)
return prov
}
ここで重要なのは、「ベースとなるTerraformプロバイダ(ここでは `terraform-provider-internal-user`)」がすでに存在するか、あるいは自作していることです。もしTerraformプロバイダの書き方に興味がある方は、Terraform Plugin SDKを使った簡単なAPIクライアント実装を想像してください。それだけで、Pulumiプロバイダの土台が完成します。
ステップ 4: プロバイダのビルドとローカルインストール
コードを書いたら、いよいよビルドです。以下のコマンドで、マルチプラットフォーム用のプロバイダバイナリと、各言語向けのSDKが自動生成されます。
プロバイダのビルドとローカルへのインストール
make install
これによって、あなたのローカル環境の Pulumi エンジンが新しい `internaluser` プロバイダを認識し、TypeScriptやPythonからインポートできるようになります。
—
4. 精度高い「HelloWorld」:自作プロバイダを使ってみる
さて、いよいよ待ちに待った動作確認です!
適当な作業用ディレクトリを作成し、先ほど作ったカスタムプロバイダを使って、社内API経由でユーザーを作成するPulumiプログラム(TypeScript)を書いてみましょう。
1. プロジェクトの初期化
mkdir my-infra-test
cd my-infra-test
pulumi new typescript –dir . –yes
2. 自作プロバイダのSDKをインストール
先ほどビルドしたローカルのプロバイダSDKをプロジェクトに追加します。
npm install ./sdk/nodejs/bin # (※パスは環境に応じて調整)
3. `index.ts` の記述
それでは、社内APIに対してユーザーを作成するコードを記述します。見慣れたTypeScriptのコードでインフラ(リソース)が宣言できる感動を味わってください。
import as pulumi from “@pulumi/pulumi”;
import as internaluser from “@your-org/pulumi-internal-user”;
// プロバイダの設定(社内APIのエンドポイントや認証トークンを渡す)
const provider = new internaluser.Provider(“api-provider”, {
endpoint: “https://api.internal.net/v1”,
apiToken: process.env.INTERNAL_API_TOKEN, // 環境変数から安全に取得
});
// 社内API経由で新しいユーザーリソースを宣言的に作成
const johnDoe = new internaluser.User(“john-doe”, {
username: “johndoe”,
email: “john.doe@example.internal”,
role: “admin”,
}, { provider: provider });
// データの出力
export const userId = johnDoe.id;
export const userStatus = johnDoe.status;
4. デプロイ実行!
pulumi up
おっと、コンソールに何が表示されるでしょうか?
Pulumiエンジンが賢くプランを計算し、背後で自作のカスタムプロバイダ(gRPCサーバー)を呼び出し、社内APIに対して `POST /v1/users` リクエストを飛ばしてリソースを構築します。
実行が成功したら、次は `pulumi destroy` を試してみてください。社内APIへの `DELETE` リクエストが綺麗に走り、リソースが安全に消去されます。
――どうですか? 手動スクリプトや散らかったAPIコールとはおさらばです。すべてがPulumiのステート管理下に収まり、冪等性が完全に担保された瞬間です。
—
5. 先輩エンジニアからのアドバイス:現場で失敗しないための極意
最後に、このカスタムプロバイダ開発を実際のプロダクション環境や社内ニッチシステムに導入する際、知っておくべき「現場の知見」をいくつか授けます。
1. 冪等性(Idempotency)をAPI側、あるいはProvider側で必ず担保する
ネットワークの切断やタイムアウトでリトライが発生した際、「同じリクエストが2回来ても安全か(二重作成されないか)」を意識してください。TerraformのProviderフレームワークを使う場合、`Read` メソッドの精度が命です。APIが返すIDやユニークキーを元に、リソースの存在確認を確実に行えるように実装しましょう。
2. 機密情報のハンドリング(Secret管理)
社内APIのアクセストークンやパスワードは、必ずPulumiのConfig(`pulumi config set –secret`)や環境変数経由で渡すようにし、プレーンテキストでコードに直書きしないこと。プロバイダ側のスキーマ定義でも、該当フィールドに `Sensitive: true` の属性を忘れないようにしましょう。
—
まとめ
今回は、Pulumiのカスタムプロバイダ開発(Bridge技術)について、その役割から具体的な実装、そしてHelloWorldまでの流れを駆け足で解説しました。
- Pulumiのプロバイダは、実態はgRPCサーバーである
- Terraformの資産があるなら、Bridge技術を使えば爆速でPulumi用プロバイダに変換できる
- 社内レガシーシステムや独自REST APIも、これで美しくIaC化(宣言的管理)できる
「世の中にないなら、自分たちでコードを書いて作ってしまえばいい」。この自由度とパワーを手に入れたあなたなら、どんなインフラストラクチャの要件が来ても怖くありませんね。
毎日のインフラ運用・構築作業が劇的に楽になり、コードを書く楽しさが何倍にも膨らむはずです。
ぜひ、あなたの会社のレガシーな秘密兵器を、Pulumiでモダンに現代へ召喚してみてください。それでは、素晴らしいIaCライフを!