はじめに
Pulumi は、AI で Pulumi を扱うための公式のツールを 2 つ提供しています。
- Pulumi MCP Server:Pulumi Cloud や Registry を Claude から直接調べるための窓口
- Pulumi プラグイン(Agent Skills):Pulumi の作業手順をまとめた手順書(Skill)のセット
この 2 つを Claude Code に入れて、それぞれで何ができるのかを実際に試してみました。
用語の整理
Pulumi を知らない人のために簡単に Pulumi のサービスの用語だけ簡単に整理しておきます。
| サービス | ひとことで | たとえば |
|---|---|---|
| Pulumi Cloud | スタックの状態や更新履歴を保存しておく Pulumi の Web サービス | 「いつ誰が pulumi up して、何が失敗したか」が残る |
| Pulumi Registry | AWS や Azure などのプロバイダーの仕様を集めたカタログ | 「S3 バケットにはどんな引数がある?」を調べる |
| Pulumi Neo | Pulumi Cloud 上で動く Pulumi の AI エージェント(使った分だけ課金) | 「この変更を staging に適用して」とタスクを任せる |
MCP とプラグインとについてざっくり説明
MCP は「調べる」担当、
Skill は「Pulumi の作法どおりに作業する」担当
です。
| Pulumi MCP Server | Pulumi プラグイン | |
|---|---|---|
| 中身 | ツール 14 個 | 手順書(SKILL.md)15 個 |
| ひとことで | Pulumi のサービスに問い合わせる | Pulumi 公式の手順で作業する |
| 動く場所 | Pulumi がホストするリモートサーバー | 手元の Claude Code が読み込み、CLI を実行 |
導入方法
MCP Server
MCP Server は、URL を登録してから Claude Code の中で /mcp を開き、pulumi を選んでブラウザで認証します。認証画面では Pulumi のアクセストークンを貼り付け、使う組織を選びます。
claude mcp add --transport http pulumi https://mcp.ai.pulumi.com/mcp
プラグイン
プラグインは、マーケットプレイスを追加してからインストールします
claude plugin marketplace add pulumi/agent-skills
claude plugin install pulumi@pulumi-agent-skills
なお、マーケットプレイスには 4 つのプラグインがあります。
今回入れた pulumi は、下の表の上 3 つをまとめた全部入りです。
| プラグイン | 入っているスキル |
pulumi と一緒に入れる? |
|---|---|---|
pulumi |
書く・運用する・移行する・Neo に渡す(15 個) | ー |
pulumi-migration |
移行するだけ(5 個) | 不要(中身が重複する) |
pulumi-delegation |
Neo に渡すだけ(1 個) | 不要(中身が重複する) |
pulumi-package-maintenance |
プロバイダーを開発する人向け(2 個) | 必要なら追加 |
検証用のスタックを作る
ここからの検証では、東京リージョンに S3 バケットと SSM パラメータを作るスタックを使います。
前提として、Pulumi CLI と Node.js が入っていること、pulumi login で Pulumi Cloud にログインしていること、AWS CLI と同じ認証情報をセットアップ済みとします。
まず、作業用のフォルダーを作ります。
mkdir -p ~/pulumi-aws-demo/app
cd ~/pulumi-aws-demo/app
次に、プロジェクトのファイルを 4 つ作ります。
プロジェクトのファイルを作る(クリックで展開)
cat > Pulumi.yaml <<'EOF'
name: pulumi-aws-demo
runtime: nodejs
EOF
cat > package.json <<'EOF'
{
"name": "pulumi-aws-demo",
"main": "index.ts",
"dependencies": { "@pulumi/pulumi": "^3", "@pulumi/aws": "^7" },
"devDependencies": { "typescript": "^5", "@types/node": "^20" }
}
EOF
cat > tsconfig.json <<'EOF'
{
"compilerOptions": { "strict": true, "target": "es2020", "module": "commonjs", "moduleResolution": "node", "outDir": "bin" },
"files": ["index.ts"]
}
EOF
cat > index.ts <<'EOF'
import * as aws from "@pulumi/aws";
const bucket = new aws.s3.Bucket("data", { forceDestroy: true });
const appName = new aws.ssm.Parameter("appName", { type: "String", value: "pulumi-aws-demo" });
export const bucketName = bucket.bucket;
export const parameterName = appName.name;
EOF
最後に、スタック dev を作って反映します。
npm install
pulumi stack init dev
pulumi config set aws:region ap-northeast-1
pulumi up --yes
Resources: + 3 created と出れば準備完了です。
このフォルダーで claude を起動し、以降の質問を試していきます。
MCP Server でできること
ツールの種類
ツール 14 個は、問い合わせる先で 4 つに分かれます。ツールは随時変わる可能性もあるので詳細はドキュメントを参考にしましょう。
| 問い合わせる先 | できること | ツール数 |
|---|---|---|
| Pulumi Cloud | スタックやリソースの一覧・検索、メンバーやポリシー違反の確認 | 4 |
| Pulumi Registry | リソースの引数や型を調べる | 5 |
| Pulumi Neo | Neo にタスクを頼む・状況を見る | 4 |
| コード生成 | AWS にデプロイする Pulumi コードの書き方を返す | 1 |
試してみた
検証用のスタックを作ったフォルダーで claude を起動し、日本語で聞いてみました。
| 聞いたこと | 使われたツール | 返ってきた内容 |
|---|---|---|
| 「組織にどんなスタックがある?」 | get-stacks |
pulumi-aws-demo/dev などのスタックとリソース数 |
| 「resource-search で、組織全体から S3 バケットを探して」 | resource-search |
スタックごとのバケット名、リージョン、作成日時 |
| 「メンバーとロール、ポリシー違反は?」 |
get-users / get-policy-violations
|
自分 1 人(Admin)、違反 0 件 |
| 「S3 バケットの主な入力プロパティは?」 | get-resource |
aws.s3.Bucket の引数一覧と、versioning などが非推奨で別リソースに移ったこと |
プラグイン(Skill)でできること
15 個のスキルは、用途で 4 つに分かれます。
| 用途 | できること | 主なスキル(数) |
|---|---|---|
| 運用する | 失敗の調査、秘密情報の管理、プロバイダーの更新 |
pulumi-debug-failed-operation ほか(4) |
| 書く・直す | Pulumi の作法どおりにコードを書く・レビューする |
pulumi-best-practices ほか(5) |
| 移行する | Terraform / CDK / CloudFormation / ARM から乗り換える |
pulumi-terraform-to-pulumi ほか(5) |
| Neo に渡す | 作業の続きを Neo に引き継ぐ |
pulumi-neo-handoff(1) |
ここからは、「運用する」「書く・直す」「移行する」から 1 つずつ試した結果を記載します。
pulumi-debug-failed-operation:失敗の原因を記録から調べる
S3 のバケット名は全 AWS アカウントで共有なので、サンプルによくある my-bucket のような名前は、すでに誰かに使われています。 これをわざと指定して pulumi up を失敗させます。
cd ~/pulumi-aws-demo/app
cat >> index.ts <<'EOF'
const logs = new aws.s3.Bucket("logs", { bucket: "my-bucket" });
export const logsBucket = logs.bucket;
EOF
pulumi up --yes
この状態で、ふつうに直してもらいます。今回はスキル名を指定しなくても、Claude がこのスキルを自分で読み込みました。
さっき dev スタックで pulumi up が失敗しました。原因を調べて直し、pulumi up --yes で反映まで確認して
ちなみに、スキルを使えない状態(--disallowedTools Skill)でも同じ依頼をしてスキルありなしを比べてみました。どちらも直りましたが、調べ方が違うようです。
スキルを使うと、失敗した更新のイベントを pulumi api で読みにいきます。 そのため、pulumi up を再実行しなくても AWS が返した実際のエラーが分かります。
スキルなしのときは、それらしい原因をコードから推測して当てていました。
pulumi-best-practices:Pulumi 特有の落とし穴を直す
Pulumi の典型的な間違いを 3 つ入れた index.ts に置き換えて、レビューしてもらいました。
間違いを入れた index.ts に置き換える(クリックで展開)
cd ~/pulumi-aws-demo/app
cat > index.ts <<'EOF'
import * as aws from "@pulumi/aws";
const bucket = new aws.s3.Bucket("data", { forceDestroy: true });
const appName = new aws.ssm.Parameter("appName", { type: "String", value: "pulumi-aws-demo" });
const logs = new aws.s3.Bucket("logs");
bucket.bucket.apply(name => {
new aws.s3.BucketObject("readme-" + name, { bucket: name, content: "hello" });
});
const dbPassword = new aws.ssm.Parameter("dbPassword", { type: "String", value: "P@ssw0rd-demo" });
const role = new aws.iam.Role("reader", {
assumeRolePolicy: JSON.stringify({
Version: "2012-10-17",
Statement: [{ Effect: "Allow", Principal: { Service: "lambda.amazonaws.com" }, Action: "sts:AssumeRole" }],
}),
});
new aws.iam.RolePolicy("readerPolicy", {
role: role.id,
policy: JSON.stringify({
Version: "2012-10-17",
Statement: [{ Effect: "Allow", Action: "s3:GetObject", Resource: bucket.arn + "/*" }],
}),
});
export const bucketName = bucket.bucket;
export const parameterName = appName.name;
export const logsBucket = logs.bucket;
EOF
/pulumi:pulumi-best-practices index.ts をレビューして、問題があれば直して。パスワードはデモ値のまま config に入れてよいので、直したら pulumi preview で確認して
| 問題 | Before | After |
|---|---|---|
apply() の中でリソースを作る(preview に正しく出ない) |
bucket.bucket.apply(name => { new aws.s3.BucketObject(...) }) |
外に出して bucket: bucket.bucket を直接渡す |
Output(後から決まる値)を + で連結 |
IAM ポリシーの Resource: bucket.arn + "/*"
|
bucket.arn.apply(arn => JSON.stringify({ ... Resource: `${arn}/*` })) |
| パスワードを直書き | SSM パラメータに value: "P@ssw0rd-demo"(String 型) |
config.requireSecret("dbPassword") で読み、SecureString 型にする |
なお、スキルを指定しない依頼でも同じ 3 点が直りました。こうした作法は、スキルがなくても Claude がある程度知っているようです。
pulumi-terraform-to-pulumi:Terraform を state ごと移行する
Terraform で作った S3 バケットと SSM パラメータを用意して、terraform apply しておきます。移行スキルは OpenTofu(Terraform 互換のオープンソース版)の tofu コマンドも使うので、あわせて入れておきます。
Terraform の構成を作って apply する(クリックで展開)
brew install opentofu
mkdir -p ~/pulumi-aws-demo/tf
cd ~/pulumi-aws-demo/tf
cat > main.tf <<'EOF'
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 6.0"
}
}
}
provider "aws" {
region = "ap-northeast-1"
}
resource "aws_s3_bucket" "assets" {
bucket_prefix = "tf-assets-"
force_destroy = true
}
resource "aws_s3_bucket_versioning" "assets" {
bucket = aws_s3_bucket.assets.id
versioning_configuration {
status = "Enabled"
}
}
resource "aws_ssm_parameter" "app_env" {
name = "/pulumi-aws-demo/app-env"
type = "String"
value = "dev"
}
output "bucket_name" {
value = aws_s3_bucket.assets.bucket
}
EOF
terraform init
terraform apply -auto-approve
claude を起動し、次のように頼みました。
tf/ にある Terraform を Pulumi の TypeScript に移行して。移行先は migrated/ で、スタック名は dev。コードの変換だけでなく state も引き継いで、作り直しが起きないことを pulumi preview で確認して
スキル名を指定しなくても、Claude が自分でこのスキルを読み込みました。 スキルに書かれた手順は次のとおりです。
pulumi preview は「4 unchanged」(バケット、バージョニング、SSM パラメータ、スタック本体)で、既存のバケットを作り直さずにそのまま引き継げました。 生成されたコードは素直なものでした。
import * as aws from "@pulumi/aws";
const assets = new aws.s3.Bucket("assets", {
bucketPrefix: "tf-assets-",
forceDestroy: true,
});
new aws.s3.BucketVersioning("assets", {
bucket: assets.id,
versioningConfiguration: {
status: "Enabled",
},
});
new aws.ssm.Parameter("app_env", {
name: "/pulumi-aws-demo/app-env",
type: "String",
value: "dev",
});
export const bucketName = assets.bucket;
単純なコード変換(pulumi convert)を使わないよう、スキルに明記されているため、作り直しを起こさずに既存リソースを引き継げます。
後片付け
作ったスタック 2 つと、Terraform で作ったリソースを消し、作業用のフォルダーごと削除します。
移行したリソースは Pulumi 側で消してから、Terraform 側の state を片付けます。
cd ~/pulumi-aws-demo/app && pulumi destroy --yes && pulumi stack rm dev --yes
cd ~/pulumi-aws-demo/migrated && pulumi destroy --yes && pulumi stack rm dev --yes
cd ~/pulumi-aws-demo/tf && terraform destroy -auto-approve
cd ~ && rm -rf ~/pulumi-aws-demo /tmp/required-providers.json
find /tmp/ -maxdepth 1 -name 'pulumi-state*.json' -delete
まとめ
MCP で Pulumi Cloud と Registry の最新情報を調べ、Skill で Pulumi の作法どおりに作業する、ことが可能です。

