既存Terraform資産を捨てずに殺す:Pulumi Terraform BridgeでHCLモジュールを完全に掌中位に収める極意
こんにちは。大規模クラウドインフラのモダナイゼーションを牽引するテックリードの皆さん。
「組織全体で何百個ものTerraform(HCL)モジュール資産がある。しかし、TypeScriptやPythonでビジネスロジックと統合されたモダンなIaC(Pulumi)へ移行したい。だが、あの膨大なモジュールを書き直すコストなど到底捻出できない」
この絶望的なジレンマに直面していないだろうか。
よくある安直な解決策として `tf2pulumi` などのコード変換ツールを持ち出す者がいるが、あれは単なる「片道切符の使い捨てスクリプト」に過ぎない。変換した瞬間に、本流であるupstreamのTerraformモジュールのアップデート追従という永遠の地獄が始まる。
私たちが求めるべきは、コードの翻訳ではない。「既存のTerraformモジュールを、型安全なPulumiのネイティブコンポーネントとして動的に、かつゼロコストでラップし実行する」というアプローチだ。
今回は、その裏側を支える Pulumi Terraform Bridge の深淵に迫り、移行期の混沌を制圧するための実践的なアーキテクチャを伝授する。
—
1. なぜ `tf2pulumi` は実務で破綻するのか?
移行期において、多くのチームが最初に通る道が `tf2pulumi` によるHCLからTypeScript/Pythonへの一括変換だ。しかし、これには決定的な致命傷がある。
1. メンテナビリティの喪失: 変換されたコードは「読みにくいトランスパイル済みの何か」になり、人間が手で保守するには苦痛でしかない。
2. アップストリーム乖離: 元のTerraformモジュールが改修された際、差分を手動でマージし直すか、再度変換して上書きするしかなく、CI/CDパイプラインが崩壊する。
3. ステートの不整合: 既存のTerraform State(S3バックエンド等)とPulumiのStateバックエンドの混在・移行において、ロック機構やIDマッピングの破綻が頻発する。
我々はTerraformの「実績あるモジュール群(資産)」というエコシステムを捨てる必要はない。Terraformの実行エンジンをPulumiのプロセスから直接叩けばいいのだ。それを可能にするのが Pulumi Terraform Bridge である。
—
2. Pulumi Terraform Bridgeによる外部プロバイダのラップと型安全なラッパー作成
Pulumi Terraform Bridgeは、Terraform Provider(Go製)をそのままPulumiのNode.js / Python / Go / C# SDKへと変換・ブリッジするフレームワークである。これを利用すれば、任意のTerraformモジュールやカスタムProviderを、Pulumiの強力な型システムに組み込むことができる。
ここでは、既存のTerraformモジュール(例: 社内ニッチなAWS VNet構成モジュール)をPulumiからシームレスに呼び出し、IDEの補完が効く型安全なラッパーコンポーネントを作成する手順を解説する。
アーキテクチャ概要
[ Pulumi Program (TypeScript) ]
↓ (強型付けされたコンポーネント呼び出し)
[ カスタム Pulumi Package (Node.js) ]
↓ (Pulumi Terraform Bridge)
[ Terraform Provider / Module 実行層 ]
実装ステップ:型安全なラッパーの構築
まずは、プロジェクトのディレクトリ構成を以下のように整備する。モジュール単位でカプセル化し、他の開発者が「普通のPulumiコンポーネント」として扱えるようにするのがプロの技だ。
infra-project/
├── Pulumi.yaml
├── package.json
├── tsconfig.json
└── components/
└── secure-vpc/
├── index.ts # 型安全なラッパーコンポーネント
└── terraform/ # 既存のTerraformモジュール(サブモジュールとして配置 or Git submodule)
├── main.tf
├── variables.tf
└── outputs.tf
設定ファイルのベストプラクティス (`tsconfig.json`)
TypeScriptで厳格な型安全性を担保するため、以下のコンパイラオプションを強制する。
{
“compilerOptions”: {
“strict”: true,
“noImplicitAny”: true,
“strictNullChecks”: true,
“target”: “es2022”,
“module”: “commonjs”,
“moduleResolution”: “node”,
“sourceMap”: true,
“outDir”: “bin”
}
}
型安全なラッパーの実装 (`components/secure-vpc/index.ts`)
ここで、Terraformモジュールの入出力をPulumiの `Output
import as pulumi from “@pulumi/pulumi”;
import as aws from “@pulumi/aws”;
// PulumiのTerraformモジュール統合機能(pulumi/terraform-provider等を利用、またはexecBridge層)
// ここでは概念を明確にするため、PulumiのTerraform Module Integration APIを使用
export interface SecureVpcArgs {
/ VPCのCIDRブロック /
cidrBlock: pulumi.Input
/ 環境名(prod, staging等) /
environment: pulumi.Input
/ パブリックサブネットを有効化するかどうか /
enablePublicSubnets?: pulumi.Input
}
export class SecureVpc extends pulumi.ComponentResource {
public readonly vpcId: pulumi.Output
public readonly publicSubnetIds: pulumi.Output
constructor(name: string, args: SecureVpcArgs, opts?: pulumi.ComponentResourceOptions) {
super(“custom:infra:SecureVpc”, name, {}, opts);
// 既存のTerraformモジュールをパス指定で直接読み込み、実行時パラメータをバインド
// 内部的にTerraform CLI / Provider binaryが安全にプロセス間通信を行う
const tfModule = new pulumi.terraform.Module(name, {
// ローカルのTerraformモジュールパスを指定
path: “./terraform”,
variables: {
cidr_block: args.cidrBlock,
env: args.environment,
enable_public: args.enablePublicSubnets ?? true,
},
}, { parent: this });
// TerraformのOutputsをPulumiの強型付けされたプロパティとして公開
this.vpcId = tfModule.outputs.apply(o => o.vpc_id as string);
this.publicSubnetIds = tfModule.outputs.apply(o => o.public_subnet_ids as string[]);
this.registerOutputs({
vpcId: this.vpcId,
publicSubnetIds: this.publicSubnetIds,
});
}
}
このアプローチにより、呼び出し側(メインプログラム)では以下のように極めてモダンかつ安全にレガシーモジュールを再利用できる。
import { SecureVpc } from “./components/secure-vpc”;
const vpc = new SecureVpc(“core-vpc”, {
cidrBlock: “10.100.0.0/16”,
environment: “production”,
enablePublicSubnets: true,
});
// 完全な型補完とグラフ依存関係の自動解決が機能する
export const vpcId = vpc.vpcId;
—
3. 移行期におけるハイブリッド運用とベストプラクティス
TerraformからPulumiへの移行期(Migration Phase)には、同一AWSアカウント内、あるいは同一VPC内であっても「ある部分はTerraformで管理され、別の部分はPulumiで管理される」という状態が数ヶ月にわたって継続する。
この地獄のようなカオスを生き抜くための実践的なベストプラクティスを授ける。
① Stateの所有権(State Ownership)の厳格な分離
Terraformの `State` と Pulumiの `State` が同一リソースを奪い合う「デュアルマネジメント」は絶対避けること。
- 原則: 1つのリソース(例: 特定のRDSインスタンス)のライフサイクル管理は、TerraformかPulumiのどちらか片方のみが行う。
- 移行対象のリソースをPulumi側に取り込む際は、必ず既存のTerraform Stateからリソースを `terraform state rm` で切り離し、Pulumi側で `pulumi import` を実行するパイプライン(または手順書)を自動化する。
② CI/CDパイプラインにおける実行順序の制御
ハイブリッド期間中、GitHub ActionsなどのCI環境では、TerraformのApplyが完了した後にPulumiのUpを実行する依存関係(DAG)を明確にする必要がある。
実用的なGitHub Actionsワークフローの断片 (`.github/workflows/deploy.yml`)
name: Hybrid IaC Deployment
on:
push:
branches: [ main ]
jobs:
terraform-legacy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Terraform
uses: hashicorp/setup-terraform@v3
- name: Terraform Apply (Foundation Layer)
run: |
cd terraform/foundation
terraform init
terraform apply -auto-approve
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
pulumi-modern:
needs: terraform-legacy # レガシーTerraform層の完了を完全に待つ
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ’20’
cache: ‘npm’
- name: Install Pulumi CLI
uses: pulumi/action-install-pulumi@v3
- name: Pulumi Up (Application & Microservices Layer)
run: |
npm ci
pulumi stack select production
pulumi up –non-interactive –yes
env:
PULUMI_ACCESS_TOKEN: ${{ secrets.PULUMI_ACCESS_TOKEN }}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
—
4. プロの現場で生産性を爆発させるティップス
最後に、日々の開発スピードを極限まで高めるための「隠し味」を共有しよう。
絶対入れるべき神プラグイン・拡張機能
1. VS Code: `Pulumi Snippets`: リソース宣言時のボイラープレートコードを瞬時に生成。
2. VS Code: `Terraform (HashiCorp)`: ラップ元のHCLモジュールを記述・検証する際に依然として必須。
開発スピードを加速するキーボードショートカット (VS Code)
- `Ctrl + Space` (Mac: `Cmd + Space`): Pulumiのコンポーネント引数内で押下し、型定義と必須プロパティを0.5秒で呼び出す。
- `F12` -> `Go to Definition`: ラップしたTerraformモジュールの内部変数定義(`variables.tf`)へ一瞬でジャンプし、仕様確認のタイムロスをゼロにする。
—
結びにかえて:レガシーを「活かす」知性を持て
古い技術をすべてスクラップ&ビルドするのは、アマチュアのやり方だ。真のシニア・SREは、過去の遺産(Terraformモジュール)が持つ信頼性と実績を、モダンなプログラミング言語のパワー(Pulumi)で包み込み、組織全体の認知負荷を最小化しながら前進する。
Pulumi Terraform Bridgeはそのための最強の武器である。今日から君のプロジェクトでもこの手法を取り入れ、チームの生産性を圧倒的な高みへと引き上げてほしい。