【テクニカル・上級編】PulumiとOpenAI APIを連携させたIaC自動生成ボットの作り方:チャットからインフラをデプロイする実験的試み – インフラ構成管理(IaC)活用バイブル

漆黒の自動化:PulumiとOpenAI APIが生み出す「自然言語IaCパイプライン」の深淵

インフラストラクチャ・アズ・コード(IaC)の進化は、HCLやYAMLといった静的な宣言的言語の記述から、TypeScriptやPythonなどの汎用プログラミング言語を用いた動的な抽象化へとシフトした。Pulumiはその最先端に位置するツールであり、インフラを「ソフトウェア」として完全に掌握することを可能にした。

だが、エンジニアがコードを書くという行為そのものがボトルネックになる時代は終わろうとしている。
本稿では、OpenAI APIのFunction Calling(Structured Outputs)を駆使し、人間の曖昧な自然言語プロンプトから厳密な型を持つPulumiプログラムを動的に生成、サンドボックス上で安全にプレビュー・適用する「自律型インフラボット」のアーキテクチャを解剖する。

単なる「AIのおもちゃ」ではない。本番環境への導入を見据え、セキュリティ、冪等性、そしてLLMの非決定性をどう飼いならすか。その極限の知見をここに開示する。

—

1. 全体アーキテクチャ:LLMとPulumiの融合点

自然言語からインフラをデプロイするパイプラインにおいて、最大の敵は「LLMのハルシネーション(幻覚)」と「未検証コードの実行リスク」である。

我々が構築するシステムは、以下のパイプラインを非同期かつ安全に実行する。

[ Slack / CLI ]
│ (自然言語プロンプト)
▼
[ API Gateway / Worker ]
│
▼
[ OpenAI API (Structured Outputs) ]
│ (厳格なJSONスキーマ)
▼
[ Code Generator (TypeScript AST) ]
│ (安全なPulumiコード片の組み立て)
▼
[ Isolated Sandbox (Docker / gVisor) ]
│
├─> `pulumi preview` (差分検証)
└─> `pulumi up –non-interactive` (適用)

このアーキテクチャの核心は、LLMに直接コード文字列を書かせないことにある。LLMには「インフラストラクチャの抽象モデル(JSON)」を出力させ、その構造化データから決定論的なコードジェネレーターがPulumiのTypeScriptコードを組み立てる。これにより、構文エラーや不正なAPIの呼び出しを根本から根絶する。

—

2. OpenAI Structured Outputsによる「型安全なインフラ定義」

LLMの出力が毎回異なるフォーマットであれば、自動化パイプラインは一瞬で破綻する。OpenAI APIの `response_format`(JSON Schema強制)を用い、出力されるJSONのスキーマを厳密に定義する。

以下は、AWSのVPCおよびEC2インスタンスを生成するためのスキーマを強制するPythonコードの核心部分だ。

import json
from openai import OpenAI
from pydantic import BaseModel, Field

client = OpenAI()

1. LLMが出力すべきインフラの抽象モデルをPydanticで定義
class EC2Spec(BaseModel):
instance_type: str = Field(description=”AWS EC2 instance type, e.g., t3.micro”)
ami_id: str = Field(description=”AMI ID for the instance”)
disk_size_gb: int = Field(default=20)

class VPCSpec(BaseModel):
cidr_block: str = Field(description=”CIDR block for the VPC, e.g., 10.0.0.0/16″)
enable_dns_hostnames: bool = True

class InfrastructureIntent(BaseModel):
stack_name: str
vpc: VPCSpec
ec2_instances: list[EC2Spec] = Field(default_factory=list)

def interpret_prompt(prompt: str) -> InfrastructureIntent:
“””
自然言語のプロンプトを厳密なインフラ意図(Intent)に変換する
“””
completion = client.beta.chat.completions.parse(
model=”gpt-4o”,
messages=[
{
“role”: “system”,
“content”: “あなたは世界最高峰のCloud Architectです。ユーザーの要望からAWSインフラストラクチャの構成を抽出し、指定されたスキーマに従って出力してください。”
},
{“role”: “user”, “content”: prompt},
],
response_format=InfrastructureIntent,
)
return completion.choices.message.parsed

このアプローチにより、LLMは「嘘のプロパティ名」や「存在しないリソースタイプ」を出力できなくなる。スキーマ違反のレスポンスはAPI層で弾かれるため、後続のコード生成器は完全に信頼できるデータを処理できる。

—

3. 決定論的コードジェネレーターの実装(TypeScript)

LLMから受け取った構造化データ(JSON)を、PulumiのTypeScriptプログラムへ変換する。文字列結合でコードを書くのはアマチュアのやることだ。ここでは安全かつ拡張性の高いコード生成のロジックを示す。

import as fs from ‘fs’;
import as path from ‘path’;

interface InfrastructureIntent {
stackName: string;
vpc: {
cidrBlock: string;
enableDnsHostnames: boolean;
};
ec2Instances: Array<{ instanceType: string; amiId: string; diskSizeGb: number; }>;
}

export function generatePulumiProgram(intent: InfrastructureIntent, outputDir: string): void {
// 決定論的にPulumiのTypeScriptコードを組み立てる
const code = `
import as pulumi from “@pulumi/pulumi”;
import as aws from “@pulumi/aws”;

// Generated by AI Infra Bot for Stack: ${intent.stackName}
export async function main() {
// 1. VPCの構築
const vpc = new aws.ec2.Vpc(“${intent.stackName}-vpc”, {
cidrBlock: “${intent.vpc.cidrBlock}”,
enableDnsHostnames: ${intent.vpc.enableDnsHostnames},
tags: {
Name: “${intent.stackName}-vpc”,
ManagedBy: “Pulumi-AI-Bot”,
},
});

// 2. インターネットゲートウェイとサブネットの省略 (簡略化のため直接インスタンスへ)
// 3. EC2インスタンスの動的生成
const instances = [];
$
{
intent.ec2Instances.map((ec2, idx) => `
const inst${idx} = new aws.ec2.Instance(“${intent.stackName}-inst-${idx}”, {
ami: “${ec2.amiId}”,
instanceType: “${ec2.instanceType}”,
rootBlockDevice: {
volumeSize: ${ec2.diskSizeGb},
volumeType: “gp3”,
},
tags: {
Name: “${intent.stackName}-instance-${idx}”,
},
});
instances.push(inst${idx}.id);
`).join(‘\n’)
}

return {
vpcId: vpc.id,
instanceIds: instances,
};
}
`;

fs.writeFileSync(path.join(outputDir, ‘index.ts’), code.trim());
}

このコードジェネレーターは、LLMの出力揺れを一切受け付けない。テンプレート化されたコード構造の中に、検証済みのパラメータのみを埋め込むため、構文エラー(Syntax Error)の発生確率はゼロになる。

—

4. サンドボックス環境での動的実行とプレビューの自動化

生成されたコードは、そのまま本番環境に適用してはならない。必ず隔離されたエフェメラルなコンテナ(サンドボックス)上で `pulumi preview` を実行し、変更差分(Diff)を人間に提示、あるいはポリシーチェック(Opa / Checkovなど)にかける必要がある。

以下は、Docker APIを叩いて一時的なコンテナ内でPulumiを実行し、その標準出力(Preview結果)をキャプチャするNode.jsの自動化スクリプトである。

import { execSync } from ‘child_process’;
import as crypto from ‘crypto’;

interface ExecutionResult {
stdout: string;
stderr: string;
exitCode: number;
}

export function runPulumiPreview(projectDir: string, stackName: string): ExecutionResult {
const backendUrl = process.env.PULUMI_BACKEND_URL || “s3://my-pulumi-state-bucket”;

try {
// 1. スタックの初期化(存在しない場合のみ)
execSync(`pulumi stack init ${stackName} –non-interactive`, { cwd: projectDir, stdio: ‘ignore’ });

// 2. プレビューの実行(JSON形式で出力を取得するのがプロダクションの鉄則)
const stdout = execSync(`pulumi preview –show-saturations –json –non-interactive`, {
cwd: projectDir,
env: {
…process.env,
PULUMI_BACKEND_URL: backendUrl,
},
maxBuffer: 1024 1024 10, // 10MBバッファ
}).toString();

return {
stdout,
stderr: “”,
exitCode: 0,
};
} catch (error: any) {
return {
stdout: error.stdout?.toString() || “”,
stderr: error.stderr?.toString() || “”,
exitCode: error.status || 1,
};
}
}

🧠 エキスパートの知見:メモリ消費とステートロックの競合回避

大規模なインフラストラクチャーを動的生成する場合、Node.jsプロセスのメモリフットプリント(RSS)が膨れ上がることがある。特に Pulumi Engine は大量のリソースグラフをメモリ上に展開するため、 `–max-old-space-size` を明示的に指定してV8エンジンのヒープ制限を調整することが不可欠だ。

また、ボットからの同時リクエストによってPulumi Backendのステートロック(State Lock)が競合するリスクを防ぐため、RedisやDynamoDBベース分散ロック機構をサンドボックス層の手前に挟む設計が求められる。

—

5. 実務における安全な適用可能性とガバナンス

「チャットからインフラを生やす」というアプローチは、デモとしては魅力的だが、エンタープライズ環境においては厳格なガバナンスの枠内に収める必要がある。

1. ポリシーアズコード(OPA / Conftest)の強制
LLMが生成し、PulumiがプレビューしたJSONプランに対し、デプロイ前に必ずポリシーチェックを走らせる。「パブリックIPを持つセキュリティグループの許可」「タグの強制付与」「コスト上限(月額コスト試算)」に違反している場合、自動的にパイプラインを即座に破棄(Abort)する。
2. 「Human-in-the-Middle(HIL)」の原則
Slack等のチャットボット経由でインフラ構築を指示した場合でも、最終的な `pulumi up` の実行ボタン(あるいは承認リアクション)は、権限を持ったインフラエンジニアの承認を必須とする。AIが実行するのはあくまで「提案(Preview)」までである。

—

結び:インフラ自動化のパラダイムシフト

PulumiとLLMの融合は、単に「コードを書く手間を省く」ものではない。
それは、人間がビジネス要件(「セキュアな3階層Webアプリ環境が今すぐ欲しい」)を投げ、インフラストラクチャが自律的にその姿を形作る「Intent-Driven Infrastructure(意図駆動型インフラ)」の夜明けである。

このアーキテクチャをあなたの組織のパイプラインに組み込むことで、インフラプロビジョニングのリードタイムは数日から数秒へと短縮される。だが忘れてはならない。その裏側でコードの安全性を担保し、ステートを極限まで最適化するのは、いつの時代も我々インフラエンジニアの知見と執念に他ならない。

タイトルとURLをコピーしました