2026年7月2日
2026年7月2日
Pulumiでインフラをコード化する方法|TypeScriptでAWSを構築する手順
Pulumi は、TypeScript や Python などの一般的なプログラミング言語でクラウドのインフラを定義し、pulumi up 1回で作成・変更できるIaC(Infrastructure as Code)ツールです。この記事では、インストールからプロジェクトとスタックの作成、AWS 上にWebサーバー用のセキュリティグループとEC2インスタンスを定義するコード、設定値と機密値の管理、stateの保存先の選び方までを、つまずきやすい点と確認方法を添えて説明します。
Pulumi の考え方と Terraform との違い
Pulumi も Terraform も「あるべき構成を宣言し、ツールが現状との差分を計算して適用する」という点は同じです。違いは記述言語にあります。Terraform は HCL という専用の言語で書きますが、Pulumi はTypeScript・Python・Go・C#・Java などで書け、YAML にも対応しています(2026年10月現在)。
一般的な言語で書けると、次のような場面で楽になります。
- ループや条件分岐、関数を普段の書き方で使える
- 共通部品をクラスやパッケージとして切り出し、社内で再利用できる
- エディタの型補完が効き、プロパティ名の打ち間違いに書いている途中で気付ける
- 普段のテストツールで単体テストを書ける
一方で、自由度が高いぶん、チーム内で書き方をそろえないとコードが読みにくくなります。「リソースの定義以外の処理(外部APIの呼び出しなど)は入れない」といった約束を最初に決めておくと、後で困りにくくなります。
用語も押さえておきます。
- プロジェクト:
Pulumi.yamlがあるディレクトリ。コードの単位 - スタック:プロジェクトを動かす環境の単位(dev、staging、prod など)。stateと設定値はスタックごとに持つ
- バックエンド:stateの保存先。Pulumi Cloud か、自分で用意したストレージ
インストールとログイン
Linux や macOS では公式のインストールスクリプト、macOS では Homebrew も使えます。Windows には公式のインストーラーやパッケージマネージャー経由の方法が用意されています。最新の手順は公式ドキュメントの Download & install のページで確認してください。
# Linux / macOS の例
curl -fsSL https://get.pulumi.com | sh
# macOS(Homebrew)の例
brew install pulumi/tap/pulumi
pulumi version
TypeScript で書く場合は Node.js も必要です。AWS を操作するので、AWS CLI のプロファイルか環境変数で認証情報を使える状態にしておきます。
次にバックエンドへログインします。何も指定しなければ Pulumi Cloud が使われます。Pulumi Cloud を使わず、自分の S3 バケットや手元のディレクトリにstateを置くこともできます。
# Pulumi Cloud を使う
pulumi login
# 自前の S3 バケットに置く(バケットは事前に作成し、バージョニングを有効に)
pulumi login s3://your-pulumi-state-bucket
# 手元で試すだけなら
pulumi login --local
Pulumi Cloud は履歴の閲覧や同時実行のロック、チームの権限管理が最初から使えます。自前の保存先は外部サービスに依存しない反面、バックアップや権限設計を自分で担う必要があります。どちらにするかは、チームの規模と社内の方針で決めてください。
プロジェクトとスタックを作る
空のディレクトリで、テンプレートからプロジェクトを作ります。
mkdir web-infra && cd web-infra
pulumi new aws-typescript
対話形式でプロジェクト名、スタック名(例: dev)、AWS のリージョンを聞かれます。終わると Pulumi.yaml、Pulumi.dev.yaml、index.ts、package.json などが作られます。Pulumi.dev.yaml には dev スタックの設定値が入ります。
本番用のスタックは後から追加できます。
pulumi stack init prod
pulumi config set aws:region ap-northeast-1
pulumi stack ls
Webサーバーの構成をコードで書く
index.ts を次のように書き換えます。Ubuntu の公式イメージを検索し、HTTP/HTTPS を公開、SSH は指定したIPからのみ許可するセキュリティグループを付けて EC2 インスタンスを作る例です。
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const config = new pulumi.Config();
const instanceType = config.get("instanceType") || "t3.micro";
// SSH を許可する接続元(例: 203.0.113.5/32)。未設定ならエラーにして止める
const sshCidr = config.require("sshCidr");
// Canonical が公開している Ubuntu 24.04 の最新イメージを探す
const ami = aws.ec2.getAmiOutput({
mostRecent: true,
owners: ["099720109477"],
filters: [
{ name: "name", values: ["ubuntu/images/hvm-ssd-gp3/ubuntu-noble-24.04-amd64-server-*"] },
],
});
const sg = new aws.ec2.SecurityGroup("web-sg", {
description: "Web server",
ingress: [
{ protocol: "tcp", fromPort: 80, toPort: 80, cidrBlocks: ["0.0.0.0/0"] },
{ protocol: "tcp", fromPort: 443, toPort: 443, cidrBlocks: ["0.0.0.0/0"] },
{ protocol: "tcp", fromPort: 22, toPort: 22, cidrBlocks: [sshCidr] },
],
egress: [
{ protocol: "-1", fromPort: 0, toPort: 0, cidrBlocks: ["0.0.0.0/0"] },
],
});
const web = new aws.ec2.Instance("web", {
ami: ami.id,
instanceType: instanceType,
vpcSecurityGroupIds: [sg.id],
tags: { Name: `web-${pulumi.getStack()}` },
});
export const publicIp = web.publicIp;
ポイントは2つあります。1つ目は、SSH の接続元を設定値にしていることです。0.0.0.0/0 で SSH を開けたまま放置するのは避けてください。2つ目は、web.publicIp のような値が「Output」という型になっていることです。インスタンスが作られるまで値が決まらないため、普通の文字列のように連結すると期待どおりになりません。値を加工したい場合は pulumi.interpolate や .apply() を使います。ここは Pulumi を書き始めた人が最も引っかかりやすい部分です。
イメージ名の命名規則やインスタンスタイプの提供状況はリージョンや時期によって変わるので、動かない場合は AWS 側の表示を確認してください。
設定値と機密値
コードに書いた config.get / config.require の値は、スタックごとに設定します。
pulumi config set sshCidr 203.0.113.5/32
pulumi config set instanceType t3.small
データベースのパスワードのような値は --secret を付けます。設定ファイルには暗号化された状態で保存され、コード側では config.requireSecret("dbPassword") で読み出します。
pulumi config set --secret dbPassword
暗号化の鍵は、既定では Pulumi Cloud が管理します。自前のバックエンドを使う場合はパスフレーズ方式(環境変数 PULUMI_CONFIG_PASSPHRASE で渡す)か、AWS KMS などのクラウドの鍵管理サービスを --secrets-provider で指定します。パスフレーズを失うと機密値を復号できなくなるので、パスワード管理ツールなどに保管してください。
なお、機密値として扱っても、リソースの属性として使えばstateの中には暗号化された形で記録されます。stateの保存先へのアクセス権を絞ることも合わせて考えてください。
プレビューして適用する
適用前に差分を確認します。
pulumi preview
作成・変更・削除されるリソースが一覧で表示されます。「replace(作り直し)」が出ているリソースは、いったん削除されてから作られるため、本番では停止時間が出る可能性があります。意図しない replace が出たら適用せず、原因となったプロパティを確認してください。
問題なければ適用します。
pulumi up
pulumi stack output publicIp
pulumi up も実行前にプレビューを表示し、確認を求めます。CI から実行する場合は --yes で確認を省けますが、その場合はプルリクエストの段階で pulumi preview の結果をレビューする流れを作っておくと安全です。
運用でよく使う操作
- ドリフトの確認:誰かがコンソールから手で変更した場合、
pulumi refreshで実際の状態をstateに取り込めます。差分を見てから、コードに反映するか元に戻すかを決めます。 - 既存リソースの取り込み:手で作ったリソースを管理下に置くには
pulumi importを使います。いきなり本番で行わず、検証用のスタックで手順を確かめてください。 - Terraform からの移行:
pulumi convert --from terraformで HCL を Pulumi のコードに変換する機能があります。変換結果はそのまま使わず、レビューしてから取り込みます。 - 後片付け:検証用の環境は
pulumi destroyでリソースを削除し、不要になったスタックをpulumi stack rmで消します。本番スタックで誤って実行しないよう、作業前にpulumi stack lsで現在のスタックを確認する習慣を付けてください。
まとめ
- Pulumi は一般的なプログラミング言語でインフラを定義でき、型補完や部品化の恩恵を受けられる
- プロジェクトはコードの単位、スタックは環境の単位で、stateと設定はスタックごとに持つ
- stateの保存先は Pulumi Cloud か自前のストレージから、運用体制に合わせて選ぶ
- Output 型の値は
pulumi.interpolateや.apply()で扱う - 機密値は
--secretで設定し、暗号化の鍵やパスフレーズの保管も計画しておく - 適用前に
pulumi previewで replace の有無を確認する