TerraformからPulumiへの移行ガイド:State移行とコード変換の極意
テックリードの私たちが、なぜ今HCL(HashiCorp Configuration Language)の呪縛を断ち切り、Pulumiへの移行を進めるのか。その理由はシンプルだ。「インフラ構築を、真のソフトウェアエンジニアリングに取り戻すため」に他ならない。
条件分岐のために不格好な `count` や `for_each` を書き、型安全ではない文字列の結合に怯え、巨大化した `terraform.tfstate` のコンフリクトに頭を抱える日々はもう終わりにしよう。TypeScriptやPythonのフルパワー、強力な型システム、そしてテスト駆動インフラストラクチャの世界へようこそ。
本稿では、既存のTerraform資産を安全かつ高速にPulumiへ移行するための実践的な知見を、泥臭いStateの扱いやコード変換の戦略を含めて徹底解説する。
—
1. TerraformのStateファイルをPulumiへ移行するアプローチ
TerraformからPulumiへの移行において、最も恐れられているのが「既存リソースの破壊(ダウンタイム)」だ。結論から言えば、既存の `terraform.tfstate` を直接PulumiのStateに変換する魔法のコンバーターは存在しない。
しかし、プロフェッショナルには「ゼロダウンタイムでインフラの所有権を移行する」ための定石がある。それが `pulumi import` と既存リソースのインポート戦略 だ。
移行の基本フロー
1. Terraformで管理していたリソースをそのまま残した状態で、対応するPulumiコードを記述する。
2. Pulumiの `import` オプション(または `pulumi import` コマンド)を使用し、既存のクラウド実体をPulumiのStateに取り込む。
3. Terraform側から該当リソースの記述を削除(`terraform state rm`)する。
このアプローチにより、クラウド上のリソースを一度も削除・再作成することなく、管理権限をTerraformからPulumiへアトミックに移管できる。
—
2. HCLからTypeScript/Pythonへのコード書き換え戦略
HCLから汎用プログラミング言語(ここではエコシステムの豊かさからTypeScriptを推奨)への移行は、単なる「構文の置き換え」ではない。設計思想のパラダイムシフトだ。
HCLの限界をTypeScriptでどう突破するか
例えば、複数のVPCサブネットを動的に生成するコードを考えてみよう。Terraformでは `for_each` と複雑なCIDR計算関数を駆使していたものが、TypeScriptでは標準の配列操作関数(`map`, `filter`)と強力な型推論によって、圧倒的に直感的かつ堅牢になる。
実用的な設定・コードのベストプラクティス構成例(TypeScript)
プロダクション環境で耐えうる、型安全かつモジュール化されたプロジェクト構造の例を示す。
.
├── Pulumi.yaml # プロジェクトメタデータ
├── Pulumi.prod.yaml # プロダクション用スタック設定
├── package.json # 依存関係管理
├── tsconfig.json # TypeScript設定
└── src/
├── index.ts # エントリーポイント
├── config.ts # 型安全な設定ローダー
└── networking/
└── vpc.ts # ネットワークコンポーネント
`src/config.ts` – 型安全な設定値のロード
環境変数やPulumi Configを直接コード内に散在させず、Zodや素のTypeScriptの型ガードを用いてパース・検証する。
import as pulumi from “@pulumi/pulumi”;
interface Config {
environment: string;
vpcCidr: string;
maxAzs: number;
}
// PulumiのConfigオブジェクトから安全に値を取得
const pulumiConfig = new pulumi.Config();
export const config: Config = {
environment: pulumiConfig.require(“environment”),
vpcCidr: pulumiConfig.require(“vpcCidr”),
// 型安全な数値パース(不正な値なら即座にデプロイを失敗させる)
maxAzs: pulumiConfig.getNumber(“maxAzs”) ?? 2,
};
`src/networking/vpc.ts` – カプセル化されたリソース定義
クラスや関数を用いてインフラを抽象化する。これがPulumiの真骨頂だ。
import as aws from “@pulumi/aws”;
import as pulumi from “@pulumi/pulumi”;
interface VpcArgs {
cidrBlock: string;
environment: string;
}
export class SecureVpc extends pulumi.ComponentResource {
public readonly vpc: aws.ec2.Vpc;
public readonly publicSubnets: aws.ec2.Subnet[] = [];
constructor(name: string, args: VpcArgs, opts?: pulumi.ComponentOptions) {
super(“custom:net:SecureVpc”, name, {}, opts);
// VPCの作成
this.vpc = new aws.ec2.Vpc(`${name}-vpc`, {
cidrBlock: args.cidrBlock,
enableDnsHostnames: true,
enableDnsSupport: true,
tags: {
Environment: args.environment,
ManagedBy: “Pulumi”,
},
}, { parent: this });
// ComponentResource内部でリソースを構築することで、
// 外部からは「1つのコンポーネント」としてきれいに隠蔽される
this.registerOutputs({
vpcId: this.vpc.id,
});
}
}
—
3. 移行時に発生しやすいトラブルと回避策
現場でよくある失敗パターンと、それを回避するためのプロの知見を共有する。
トラブル1: プロバイダーの暗黙的な依存関係の崩壊
- 現象: Terraformではよしなに解決されていたリソース間の暗黙的な順序依存が、Pulumiで非同期処理(Promise)の評価順序のせいでデッドロックや作成順序エラーを起こす。
- 回避策: `dependsOn` オプションを明示的に指定するか、出力プロパティ(`Output
`)を別のリソースの入力として直接渡すことで、Pulumiのエンジンに正しい依存グラフを認識させること。`.apply()` を乱用せず、極力プレースホルダーやInput型を活用せよ。
トラブル2: セキュアな設定(Secrets)の平ール漏洩
- 現象: HCLの `sensitive = true` に甘えていたエンジニアが、TypeScriptのコード上で機密情報を誤って `console.log` し、CI/SaaSのログに平文で出力してしまう。
- 回避策: Pulumiのシークレット管理(`pulumi.secret()`)を徹底し、出力値は必ず暗号化コンテナとして扱う。後述のプラグインや静的解析を導入し、CI上でブロックする仕組みを強制する。
—
4. 段階的な移行を進めるためのベストプラクティス
ビッグバン移行(全システムの一斉リプレイス)はSREのアンチパターンだ。モジュール単位、あるいはレイヤー単位で段階的に移行せよ。
段階的移行のロードマップ
1. 基盤レイヤー (Networking / IAM): 変更頻度が低く、依存関係の根幹となるリソースを `pulumi import` で徐々に移行。
2. データレイヤー (RDS / S3): データの安全性に細心の注意を払いながら、既存のTerraform StateからPulumiへ所有権を移転。
3. アプリケーションレイヤー (ECS / EKS / Lambda): デプロイ頻度が高く、HCLの表現力不足に最も悩まされていた領域を最後に移行し、ここでTypeScriptの恩恵を最大限に受ける。
—
開発スピードを爆発的に高めるプロの環境設定
最後に、チーム全体の開発体験(DX)を極限まで引き上げるための実践知を授ける。
隠れたキーボードショートカット & コマンド
- `pulumi refresh –preview`: クラウド上の実際の状態とPulumiのStateのズレ(ドリフト)を、デプロイせずに一発で検知する。日々のヘルスチェックに必須。
- `pulumi cancel`: デプロイがスタックした際の強制ロック解除。Terraformの `force-unlock` で苦しんでいた暗黒時代に別れを告げよう。
絶対入れるべき神プラグイン・ツール
1. Pulumi AI (CLI / Web): 「こんな構成のAWS ECSクラスターをTypeScriptで書いて」と指示するだけで、ベストプラクティスに則ったPulumiコードを出力してくれる。構文リファレンスを見る時間を9割削減できる。
2. eslint-plugin-pulumi: TypeScriptのコード上で、Pulumiのアンチパターン(非同期処理の誤った扱いなど)を静的解析で弾く。
チーム開発で役立つ設定の共有化ルール
- Backendの統一: 個人ごとのローカルStateではなく、必ず Pulumi Service Backend、あるいはAWS S3 + DynamoDBなどのリモートバックエンドを `Pulumi.yaml` と環境変数で強制する。
- Policy as Code (Pulumi CrossGuard): チームメンバーが誰もセキュアでないリソース(パブリックS3バケットなど)を作れないよう、組織全体のポリシーをTypeScript/Pythonでコード化し、CIパイプラインに組み込む。
—
さあ、HCLの制約から解放された真のインフラストラクチャ・エンジニアリングを始めよう。コードはより美しく、インフラはより堅牢に、そして何より――開発スピードは劇的に加速するはずだ。