Pulumi Automation API徹底解説:コードの奥底からインフラを支配する「自製IaCプラットフォーム」の作り方
こんにちは。テックリードの私だ。
日々のインフラ運用で、こんな絶望を味わったことはないだろうか?
「開発者から『ステージング環境を生やしてほしい』とチケットが飛んできた」
「Terraform CloudやGitHub Actionsのワークフローをポチるだけの人生に、エンジニアとしての尊厳が揺らいでいる」
「マルチテナントなSaaS基盤のために、動的にインフラを生成・破棄するAPIがどうしても必要だ」
既存のIaCツール(TerraformやAWS CDKなど)は素晴らしい。しかし、それらはあくまで「静的な設定ファイルやCLI」の範疇を出ない。もし、あなたが「インフラ構築・管理能力を内包した独自のSaaS、あるいは社内セルフサービスポータル」を本気で作りたいなら、選ぶべきは一択だ。
それが Pulumi Automation API である。
今回は、このAutomation APIの深淵に潜り込み、単なる「ラッパー」を超えた、プロダクションクオリティのインフラ自動化プラットフォームを構築するための実践知を授けよう。
—
1. Automation APIの概要とユースケース:なぜCLIを捨て、コードでCLIを包むのか?
通常、Pulumiを使うときはターミナルを開き、`pulumi up`を叩くだろう。だが、Automation APIはこの常識を破壊する。Automation APIとは、Pulumiのライフサイクル(プレビュー、アップデート、リフレッシュ、破棄など)を、Go、Node.js(TypeScript)、Python、C#といった汎用プログラミング言語のプロセス内から直接APIとして呼び出す仕組みだ。
現場で即座に採用すべき3つのユースケース
1. 動的テナントプロビジョニングSaaS
新規顧客がサインアップした瞬間、データベース、専用のKubernetes命名空間、DNSレコードをプログラムから数秒で払い出す。
2. 社内インフラ・セルフサービスポータル(IDP: Internal Developer Platform)
JiraやSlack、社内Webアプリと連携し、開発者がボタン一つで「安全なサンドボックス環境」をオンデマンドで生成する。
3. インテグレーション・E2Eテストの完全自動化
CI/CDパイプラインの中で、テストの直前に完全なインフラ環境をデプロイし、テスト完了後に1ミリの残骸も残さずに破壊する。
—
2. TypeScriptで直接制御する:プロセス内からPulumiエンジンをねじ伏せる
百聞は一見に如かず。TypeScriptを用いて、コードから直接Pulumiスタックを初期化し、デプロイを実行するコアロジックを見ていこう。
ここでは、AWS上にS3バケットを動的に生成するプログラムを、APIサーバーの裏側で動かす想定のコードだ。
実践的なAutomation API制御スクリプト (`deployer.ts`)
import as pulumi from “@pulumi/pulumi”;
import { LocalWorkspace } from “@pulumi/pulumi/automation”;
import as aws from “@pulumi/aws”;
/
- 指定されたテナント名に基づいて、プログラムから動的にインフラをデプロイする関数
- @param tenantName テナント固有の識別子(例: “acme-corp”)
/
export async function provisionTenantInfrastructure(tenantName: string) {
const stackName = `production-${tenantName}`;
const projectName = “SaaS-Tenant-Base”;
try {
// 1. インメモリまたはローカルのワークスペースを構築
// ここでPulumiのバックエンド(S3/Pulumi Service等)や設定をプログラム制御する
const workspace = await LocalWorkspace.createOrSelectStack({
stackName: stackName,
projectName: projectName,
// プログラム内でインフラの定義(inlineプログラミングモデル)を完結させる
program: async () => {
// テナント専用のS3バケットを作成(パブリックアクセス完全遮断のガバナンス強制)
const bucket = new aws.s3.Bucket(`tenant-data-${tenantName}`, {
bucket: `saas-tenant-${tenantName}-bucket`,
acl: “private”,
});
// スタックの出力値としてエンドポイントをエクスポート
pulumi.export(“bucketName”, bucket.id);
pulumi.export(“bucketArn”, bucket.arn);
},
});
// 2. クラウドプロバイダーのコンフィグレーションをプログラムから動的注入
await workspace.setAllConfig(“aws:region”, { value: “ap-northeast-1” });
console.log(`[Automation API] プレビュー実行中: ${stackName}`);
// 3. 変更差分の事前プレビュー(dry-run)
const previewRes = await workspace.preview();
console.log(`[Automation API] 変更予定リソース数: ${previewRes.changeSummary}`);
// 4. デプロイの実行(pulumi up 相当)
console.log(`[Automation API] デプロイ開始: ${stackName}`);
const upRes = await workspace.up({
onOutput: (out) => console.log(`[Pulumi Engine] ${out}`),
});
console.log(`[Automation API] デプロイ成功! 出力値:`, upRes.outputs);
return {
success: true,
outputs: upRes.outputs,
};
} catch (error) {
console.error(`[Automation API Error] デプロイ失敗 (${stackName}):`, error);
throw new Error(`Infrastructure provisioning failed: ${error}`);
}
}
このアプローチの美しさは、「インフラの定義」と「実行トリガー」を完全に一つのコードベース、一つの言語エコシステム内で統合できる点にある。YAMLやHCLの文字列テンプレートを結合するような、悪夢のようなハックとは今日でサヨナラだ。
—
3. 社内向けインフラプロビジョニングWebアプリの構築例
上記のロジックをラップし、社内の開発者がSlackや専用UIから叩ける「セルフサービスAPIサーバー」をFastify(またはExpress)で構築しよう。
ベストプラクティス構成例: `infra-api-server/`
infra-api-server/
├── 📁 src/
│ ├── 📄 server.ts # APIエンドポイントの定義
│ ├── 📄 deployer.ts # 先ほどのAutomation API実行ロジック
│ └── 📁 policies/ # ガバナンス用Policy as Code (Pulumi CrossGuard)
├── 📄 package.json
├── 📄 tsconfig.json
└── 📄 .env # AWS認証情報やPulumiトークン
APIサーバーの実装 (`src/server.ts`)
import Fastify from “fastify”;
import { provisionTenantInfrastructure } from “./deployer”;
const server = Fastify({ logger: true });
interface ProvisionBody {
tenantName: string;
ownerEmail: string;
}
// テナント環境作成エンドポイント
server.post<{ Body: ProvisionBody }>(“/api/v1/tenants”, async (request, reply) => {
const { tenantName, ownerEmail } = request.body;
// バリデーション:テナント名の形式チェック(半角英数ハイフンのみ)
if (!/^[a-z0-9-]+$/.test(tenantName)) {
return reply.code(400).send({ error: “Invalid tenant name format.” });
}
request.log.info(`Received provisioning request for tenant: ${tenantName}, requested by: ${ownerEmail}`);
try {
// バックグラウンドまたは同期的にAutomation APIを実行
const result = await provisionTenantInfrastructure(tenantName);
return reply.code(200).send({
message: “Infrastructure provisioned successfully.”,
tenant: tenantName,
outputs: result.outputs,
});
} catch (error: any) {
request.log.error(error);
return reply.code(500).send({ error: error.message });
}
});
// サーバー起動
const start = async () => {
try {
await server.listen({ port: 3000, host: “0.0.0.0” });
console.log(“🚀 Self-service Infrastructure API is running on port 3000”);
} catch (err) {
server.log.error(err);
process.exit(1);
}
};
start();
—
4. 運用コスト削減とガバナンス強化のポイント:プロフェッショナルの知見
Automation APIを社内展開するにあたり、現場のテックリードとして絶対に押さえておくべき「生存戦略」を授ける。
A. 並行実行制御(コンカレンシー・ロック)の担保
Pulumiはデフォルトでバックエンド(Pulumi ServiceやS3+DynamoDBなど)にロックをかけ、同一スタックの同時実行を防ぐ。しかし、API経由で同時にリクエストが走った場合、`ConcurrentUpdateError`が発生する。
- 対策: APIレイヤー(RedisやBullMQなど)でテナント名ごとのキューイング機構を挟み、同一スタックに対するデプロイメントを直列化すること。
B. Policy as Code (CrossGuard) による強制ガバナンス
セルフサービス化の最大の恐怖は、「開発者が勝手にパブリックなS3やフルアクセスのIAMロールを生やすこと」だ。Automation APIを実行する際、プログラム側で組織のポリシー(Policies)を強制適用できる。
import { PolicyPack } from “@pulumi/policy”;
// 組織のコンプライアンスポリシー定義
export const organizationalPolicies = new PolicyPack(“sec-guardrails”, {
policies: [{
name: “s3-no-public-buckets”,
description: “S3バケットのパブリックアクセスを完全に禁止する”,
enforcementLevel: “mandatory”, // 違反時は即座にデプロイを強制停止
resourceValidation: (args, reportViolation) => {
if (args.resourceType === “aws:s3/bucket:Bucket”) {
// ACLがpublic-read等の場合、容赦なく違反報告
if (args.props.acl && args.props.acl !== “private”) {
reportViolation(“S3 buckets must not be public. Use private ACL.”);
}
}
},
}],
});
これをAutomation APIのワークスペース設定に組み込むことで、「いかなるセルフサービスUI経由のデプロイであっても、セキュリティポリシーを100%バイパス不可能な状態」を作り上げることができる。
C. チーム開発で役立つ設定の共有化ルール `.pulumi/ignore` と環境変数
CI/CDやAPIサーバーのコンテナイメージに不要なファイルを巻き込まないため、`.pulumiignore` は厳格に設定せよ。
.pulumiignore のベストプラクティス
node_modules/
.git/
.log
.env
dist/
また、認証情報はコードにハードコードせず、APIサーバーの実行環境(ECS Task RoleやKubernetes Service Account)に対して、最小権限のIAMロールをアタッチする。Automation APIは自動的に環境変数(`AWS_ROLE_ARN` など)を継承するため、シークレット管理で頭を悩ませる必要はなくなる。
—
結びにかえて:インフラを「APIの側」に引きずり込め
インフラストラクチャを「インフラエンジニアが手動やCLIで構築するもの」から、「ソフトウェアの一部としてプログラムからオンデマンドに生成するもの」へ昇華させること。それこそが、現代のSREおよびプラットフォームエンジニアリングの究極形だ。
Pulumi Automation APIはそのための最強の武器となる。
CLIの呪縛から解放され、君の手で真のセルフサービス基盤を構築し、開発チームの生産性を限界突破させてほしい。