Pulumi Transformsの深淵:開発者の手を汚さずにセキュリティとコンプライアンスを完全自動強制する方法
こんにちは。大規模クラウドインフラの設計・運用を統括するテックリードの皆さん。
「開発スピードを落とさずに、全リソースへのタグ付与と暗号化の強制をどう担保するか」
この問いに、私たちは長年頭を悩ませてきました。TerraformであればSentinelやOPA/Regoの導入、AWSであればSCPやConfigの山。しかし、それらはしばしば「開発フィードバックループの遅延」や「CI/CDパイプラインの破綻」を招きます。
Pulumiには、このジレンマを美しく、かつ強烈に解決する隠し札が存在します。それが `Transforms`(トランスフォーム) です。
今回は、Policy as Code(CrossGuard)との決定的な違いを押さえつつ、TypeScriptを用いて「開発者がセキュリティを意識すらする必要のない、完全自動化されたコンプライアンス強制基盤」を構築する極限の知見を授けます。
—
1. Pulumi Transformsの仕組みとCrossGuard(Policy as Code)との違い
まず、概念の整理から始めましょう。多くのエンジニアが混同しがちですが、Pulumiにおける「Transforms」と「CrossGuard」は、そのアプローチが根本的に異なります。
- CrossGuard (Policy as Code)
- 役割: 監査・バリデーション(検知とブロック)
- タイミング: リソースグラフの評価後(プレビュー/アップデート時)
- 挙動: ルール違反を発見した場合、デプロイを失敗(Fail)させる。
- Transforms
- 役割: 変異・インジェクション(動的書き換え)
- タイミング: リソースがインスタンス化される瞬間(ランタイム評価時)
- 挙動: 定義コードを一切変更することなく、メモリ上でリソースのプロパティ(タグ、暗号化設定、ネットワーク制約など)を強制的に書き換えてプロバイダに渡す。
なぜTransformsなのか?
CrossGuardは「お巡りさん」です。違反を見つけて怒ることはできますが、直してはくれません。一方、Transformsは「錬金術師」です。開発者が適当に書いた脆弱なリソース定義であっても、デプロイの瞬間にセキュアな形へと自動改変してクラウドへ送り出します。
開発者の自由度を奪わず、ガバナンスだけを担保する——SREにとって理想郷がここにあります。
—
2. 実装:グローバルタグ自動付与と暗号化強制のTypeScriptコード
百聞は一見にしかず。すべてのAWSリソースに対して、指定された必須タグ(コストセンター、所有者、環境)の付与と、S3バケット等の暗号化設定を強制的にインジェクションするプロダクションレディなコードを提示します。
プロジェクトのルート、あるいは共通モジュール(`utils/transforms.ts` など)として配置してください。
import as pulumi from “@pulumi/pulumi”;
/
- すべてのリソースに対して、ガバナンス要件(タグ付け、暗号化)を強制するTransform
/
export const securityComplianceTransform: pulumi.ResourceTransform = (args) => {
// 1. タグの自動注入(AWSリソースを対象とする例)
if (args.props && isTaggableResource(args.type)) {
const existingTags = args.props.tags || {};
// 組織で強制すべきマンダトリータグ
const mandatoryTags = {
“ManagedBy”: “Pulumi”,
“Environment”: pulumi.getStack(),
“CostCenter”: “CC-9999-INFRA”,
“ComplianceLocked”: “true”,
};
// 既存のタグとマージ(開発者が勝手に上書きしようとしても強制上書き可能)
args.props.tags = {
…existingTags,
…mandatoryTags,
};
}
// 2. S3バケットに対する暗号化の強制インジェクション
if (args.type === “aws:s3/bucketV2:BucketV2” || args.type === “aws:s3/bucket:Bucket”) {
// serverSideEncryptionConfigurationが未設定の場合は強制追加
if (!args.props.serverSideEncryptionConfiguration) {
args.props.serverSideEncryptionConfiguration = {
rule: {
applyServerSideEncryptionByDefault: {
sseAlgorithm: “aws:kms”,
// 組織共通のKMSマスターキーを指定
kmsMasterKeyId: “arn:aws:kms:us-east-1:123456789012:key/your-org-default-key”,
},
bucketKeyEnabled: true,
},
};
}
}
// 3. EBSボリュームの暗号化強制
if (args.type === “aws:ebs/volume:Volume”) {
if (args.props.encrypted !== true) {
args.props.encrypted = true;
}
}
return { props: args.props, opts: args.opts };
};
/
- タグ付けをサポートしているリソースタイプか判定するヘルパー
/
function isTaggableResource(type: string): boolean {
// 例としてAWSを対象とする(カスタムロジックで除外判定も可能)
return type.startsWith(“aws:”);
}
適用方法(Stackレベルでのグローバル登録)
このTransformをプロジェクト全体(Stack)に適用するには、エントリーポイント(`index.ts`)の最上部、あるいは`pulumi.runtime.registerStackTransformation`を使用します。
import as pulumi from “@pulumi/pulumi”;
import { securityComplianceTransform } from “./utils/transforms”;
// スタック全体のスコープにTransformを登録
// これにより、このファイル以降(またはインポートされたモジュール内)で宣言される
// すべてのリソースにこの処理が自動適用されます。
pulumi.runtime.registerStackTransformation(securityComplianceTransform);
// — 以下、開発者は通常のコードを書くだけ —
import as aws from “@pulumi/pulumi-aws”;
// 開発者がタグを書き忘れても、勝手にタグが付与され、暗号化が有効になる
const bucket = new aws.s3.Bucket(“my-secure-bucket”, {
bucket: “app-data-bucket-unique-name”,
// tagsを指定しなくても自動でインジェクションされる!
});
—
3. 開発者が意識せずにセキュアなインフラを構築できる仕組みの作り方
Transformsを導入するだけではプロフェッショナルとは言えません。チーム全体でこの仕組みを破綻なく運用するための「実践テクニックと設定の共有化ルール」を伝授します。
A. 開発スピードを劇的に高める VSCode 設定(`settings.json`)
TypeScriptでPulumiを書く際、エディタの設定が甘いと開発体験が落ちます。以下の設定をチームのプロジェクト標準(`.vscode/settings.json`)として強制し、コーディングスピードを極限まで高めてください。
{
“editor.formatOnSave”: true,
“editor.codeActionsOnSave”: {
“source.organizeImports”: “all”
},
“typescript.suggest.completeFunctionCalls”: true,
“files.associations”: {
“Pulumi.yaml”: “yaml”
}
}
B. 絶対入れるべき神プラグイン(VSCode)
1. Pulumi (Official): リソースの補完、ホバー時のドキュメント表示に必須。
2. Error Lens: エラーや型ミスマッチをコード行のインラインに即座に表示。Transformsでのプロパティ書き換え時の型ミスを瞬殺します。
C. チーム開発のためのベストプラクティス構成例
大規模プロジェクトにおける、堅牢なディレクトリ構造と設定ファイルのベストプラクティスを共有します。ガバナンスロジック(Transforms)はインフラのビジネスロジックから完全に分離し、NPMパッケージや内部共通モジュールとして切り出すのが定石です。
my-infrastructure-project/
├── .vscode/
│ └── settings.json # チーム共通の開発環境設定
├── policies/
│ └── transforms.ts # ★ 今回解説したセキュリティ強制ロジック
├── infra/
│ ├── modules/ # 再利用可能なコンポーネント
│ │ └── secure-vpc.ts
│ ├── index.ts # エントリーポイント (registerStackTransformationを呼ぶ)
│ └── Pulumi.yaml # プロジェクト定義
├── Pulumi.staging.yaml # ステージング環境設定
├── Pulumi.production.yaml # 本番環境設定
└── package.json
`Pulumi.yaml` のベストプラクティス例
name: enterprise-cloud-infra
runtime:
name: nodejs
options:
packagemanager: npm
description: Enterprise-grade infrastructure managed with Pulumi Transforms.
config:
pulumi:tags:
value:
project: “core-banking”
team: “sre-core”
—
結び:インフラエンジニアの本当の仕事とは
私たちがやるべき仕事は、開発者一人ひとりに「タグをつけろ」「暗号化しろ」とコードレビューで小言を言うことではありません。
「安全でコンプライアンスに準拠した状態以外で作ることが技術的に不可能なシステム」をコードでデザインすることです。
Pulumiの `Transforms` を使いこなせば、開発者はビジネス価値を生むアプリケーションのインフラ定義に集中し、セキュリティやガバナンスはインフラ基盤側が裏で完璧に担保する——そんな理想的なSRE体制が手に入ります。
今すぐあなたのプロジェクトにも、この「透明なガードレール」を導入してください。コードが世界を変える瞬間を、その手で実装しましょう。