一般社団法人 全国個人事業主支援協会

COLUMN コラム

  • Pulumiで始めるプログラマブルIaC:Terraformとの比較と利点

IaCの新潮流:汎用プログラミング言語によるインフラ定義

Infrastructure as Code(IaC)と言えばTerraformが事実上の標準だが、近年Pulumiが急速に注目を集めています。PulumiはTypeScript、Python、Go、C#などの汎用プログラミング言語でインフラを定義できるツールです。筆者はTerraformを5年以上使い続けてきたが、最近の複数プロジェクトでPulumiに切り替えた経験から、両者の実践的な比較を行う。

Pulumiの基本概念

Pulumiのアーキテクチャを理解するために、まずは基本的な概念を押さえておこう。

  • Project:Pulumiプログラムのルートディレクトリ。Pulumi.yamlで定義
  • Stack:同一プロジェクトの異なる環境(dev、staging、production)を表現
  • Resource:クラウドリソースの抽象化。AWSのS3バケットやLambda関数など
  • Provider:AWS、GCP、Azureなどのクラウドプロバイダとの接続

TerraformのHCLに相当する部分を、慣れ親しんだプログラミング言語で記述できる点が最大の差別化要因だ。

TypeScriptでのインフラ定義例

実際のコードを見るのが最も理解が早い。以下はAWS上にS3バケットとLambda関数を作成する例だ。

import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";

// 設定の読み込み
const config = new pulumi.Config();
const environment = config.require("environment");

// S3バケットの作成
const bucket = new aws.s3.Bucket("data-bucket", {
bucket: \`myapp-data-\${environment}\`,
versioning: {
enabled: true,
},
tags: {
Environment: environment,
ManagedBy: "pulumi",
},
});

// Lambda関数の作成
const lambdaRole = new aws.iam.Role("lambda-role", {
assumeRolePolicy: JSON.stringify({
Version: "2012-10-17",
Statement: [{
Action: "sts:AssumeRole",
Effect: "Allow",
Principal: { Service: "lambda.amazonaws.com" },
}],
}),
});

const lambdaFunction = new aws.lambda.Function("processor", {
runtime: "nodejs18.x",
handler: "index.handler",
role: lambdaRole.arn,
code: new pulumi.asset.FileArchive("./lambda"),
environment: {
variables: {
BUCKET_NAME: bucket.id,
},
},
});

// 出力のエクスポート
export const bucketName = bucket.id;
export const functionArn = lambdaFunction.arn;

TypeScriptの型システムにより、リソースのプロパティに対する補完やバリデーションがエディタ上でリアルタイムに効く。これはHCLでは得られない体験だ。

Terraformとの詳細比較

言語と表現力

Terraformが採用するHCLは宣言的で読みやすいが、複雑なロジックの表現に限界がある。条件分岐やループが必要な場面では、しばしば可読性が低下します。

# Terraformでの条件分岐(やや読みにくい)
resource "aws_instance" "example" {
count = var.create_instance ? 1 : 0
ami = var.environment == "prod" ? var.prod_ami : var.dev_ami
}

# Pulumiなら自然なプログラミング(TypeScript)
if (shouldCreateInstance) {
const instance = new aws.ec2.Instance("example", {
ami: environment === "prod" ? prodAmi : devAmi,
});
}

Pulumiではforループ、mapfilterなどの言語機能をそのまま活用できるため、複雑なインフラパターンも自然に記述できます。

テスト戦略

Pulumiの大きなアドバンテージがテスタビリティです。汎用言語のテストフレームワークがそのまま使える。

import * as pulumi from "@pulumi/pulumi";
import { describe, it, expect } from "vitest";

describe("Infrastructure", () => {
it("S3バケットにバージョニングが有効であること", async () => {
const bucket = new aws.s3.Bucket("test-bucket", {
versioning: { enabled: true },
});

const versioning = await new Promise((resolve) =>
bucket.versioning.apply((v) => resolve(v?.enabled ?? false))
);
expect(versioning).toBe(true);
});

it("タグにEnvironmentが設定されていること", async () => {
// ユニットテストでリソースのプロパティを検証
pulumi.runtime.setMocks(new MyMocks());
const infra = await import("./index");
// アサーションロジック
});
});

Terraformにもterratestやtftest等のテストツールはあるが、言語の壁により一体感のあるテスト体験を実現するのは難しい。

状態管理

PulumiはデフォルトでPulumi Cloudにステートを保存します。S3やGCSなどのセルフホストバックエンドも選択可能だ。Terraformのリモートステート管理と基本的な概念は同じですが、Pulumi Cloudを使う場合はステート管理のセットアップが不要で、チーム間のステートロックも自動的に処理されます。

Pulumiの実戦的な強み

コンポーネントリソースによる抽象化

Pulumiでは複数のリソースをまとめたカスタムコンポーネントを作成できます。これはTerraformのモジュールに相当するが、クラスベースのため継承やコンポジションが使える。

class WebApplication extends pulumi.ComponentResource {
public readonly url: pulumi.Output;

constructor(name: string, args: WebAppArgs, opts?: pulumi.ComponentResourceOptions) {
super("custom:WebApplication", name, {}, opts);

const bucket = new aws.s3.Bucket(\`\${name}-assets\`, {
website: { indexDocument: "index.html" },
}, { parent: this });

const cdn = new aws.cloudfront.Distribution(\`\${name}-cdn\`, {
// CloudFront設定
}, { parent: this });

this.url = cdn.domainName;
}
}

// 使用側はシンプル
const app = new WebApplication("myapp", { domain: "example.com" });

シークレット管理

Pulumiはシークレットをステート内で自動的に暗号化します。pulumi.ConfigrequireSecretメソッドで取得した値は、ログやステートファイルに平文で露出しない。Terraformではこの部分をVaultなどの外部ツールで補完する必要がある場合が多いです。

移行時の注意点

TerraformからPulumiへの移行を検討する際、以下の点に留意すべきです。

  • 学習コスト:チームがプログラミング言語に習熟しているなら低い。逆にインフラ専任チームでコーディング経験が少ない場合は、HCLの方がとっつきやすい
  • エコシステム:Terraformのプロバイダ数は圧倒的。PulumiはTerraformプロバイダのブリッジ機能で補完していますが、一部機能が遅れることがある
  • 既存リソースのインポートpulumi importコマンドで既存リソースの取り込みが可能。Terraformのステートからの移行ツールも提供されています
  • CI/CDパイプライン:GitHub ActionsやGitLab CI用の公式アクションが提供されており、既存のパイプラインに組み込みやすい

まとめ

Pulumiは「プログラマのためのIaC」として、Terraformとは異なる価値を提供しています。型安全性、テスタビリティ、表現力の高さは、ソフトウェアエンジニアリングの文化が根付いたチームにとって強力な武器となります。一方で、Terraformの巨大なエコシステムと実績は依然として大きなアドバンテージだ。筆者としては、新規プロジェクトでTypeScript/Pythonに強いチームであればPulumiを積極的に検討すべきだと考えています。既存のTerraform資産がある場合は、段階的な移行を視野に入れつつ、まずは小さなプロジェクトでPulumiを試すことをお勧めします。

この記事をシェアする

  • Twitterでシェア
  • Facebookでシェア
  • LINEでシェア