mcpField
P
pulumi-component

pulumi-component

github.com/pulumiクラウド・DevOps781.5k 回インストール3 週間前に更新
リポジトリ

pulumi-component について

Pulumi ComponentResource を作成する開発者向けのスキルです。コンポーネントの構造、引数と出力の設計、複数の Pulumi 言語向けのパッケージ化を扱います。

主な機能

  1. 01コンポーネントの構造とライフサイクル
  2. 02引数インターフェースの設計
  3. 03出力とリソースの合成
  4. 04多言語コンポーネントのパッケージ化

活用シーン

  1. 01再利用可能なインフラコンポーネントの作成
  2. 02インラインリソースのコンポーネント化
  3. 03複数言語向けコンポーネントのパッケージ化

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/pulumi/agent-skills --skill pulumi-component

インストール後、Claude Code で/pulumi-componentを入力して読み込まれたか確認します。

同梱ファイルagents/openai.yamluse_cases.yaml

詳細

Pulumi コンポーネントの作成

ComponentResource は、関連するインフラストラクチャリソースを再利用可能な論理単位にまとめます。コンポーネントを使うと、インフラストラクチャを理解、再利用、保守しやすくなります。コンポーネントは、pulumi preview/pulumi up の出力や Pulumi Cloud コンソールでは、子リソースを配下に持つ単一のノードとして表示されます。

このスキルでは、コンポーネント作成のライフサイクル全体を扱います。一般的な Pulumi のコーディングパターン(Output の処理、シークレット、エイリアス、プレビューのワークフロー)については、代わりに pulumi-best-practices スキルを使用してください。

このスキルを使用するタイミング

以下の場合にこのスキルを呼び出してください。

  • 新しい ComponentResource クラスを作成する
  • コンポーネントの args インターフェースを設計する
  • 複数の Pulumi 言語からコンポーネントを利用できるようにする
  • コンポーネントパッケージを公開または配布する
  • インラインリソースを再利用可能なコンポーネントにリファクタリングする
  • コンポーネントの動作をデバッグする(出力がない、作成中のまま進まない、子リソースが誤った階層にあるなど)

コンポーネントの構成

すべてのコンポーネントには、次の4つの必須要素があります。

  1. ComponentResource を継承し、型 URN を指定して super() を呼び出す
  2. 標準パラメーターを受け取る:name、args、ComponentResourceOptions
  3. すべての子リソースに parent: this を設定する
  4. コンストラクターの最後に registerOutputs() を呼び出す
TypeScript
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";

interface StaticSiteArgs {
    indexDocument?: pulumi.Input<string>;
    errorDocument?: pulumi.Input<string>;
}

class StaticSite extends pulumi.ComponentResource {
    public readonly bucketName: pulumi.Output<string>;
    public readonly websiteUrl: pulumi.Output<string>;

    constructor(name: string, args: StaticSiteArgs, opts?: pulumi.ComponentResourceOptions) {
        // 1. Call super with type URN: <package>:<module>:<type>
        super("myorg:index:StaticSite", name, {}, opts);

        // 2. Create child resources with parent: this
        const bucket = new aws.s3.Bucket(`${name}-bucket`, {}, { parent: this });

        const website = new aws.s3.BucketWebsiteConfigurationV2(`${name}-website`, {
            bucket: bucket.id,
            indexDocument: { suffix: args.indexDocument ?? "index.html" },
            errorDocument: { key: args.errorDocument ?? "error.html" },
        }, { parent: this });

        // 3. Expose outputs as class properties
        this.bucketName = bucket.id;
        this.websiteUrl = website.websiteEndpoint;

        // 4. Register outputs -- always the last line
        this.registerOutputs({
            bucketName: this.bucketName,
            websiteUrl: this.websiteUrl,
        });
    }
}

// Usage
const site = new StaticSite("marketing", {
    indexDocument: "index.html",
});
export const url = site.websiteUrl;
Python
import pulumi
import pulumi_aws as aws

class StaticSiteArgs:
    def __init__(self,
                 index_document: pulumi.Input[str] = "index.html",
                 error_document: pulumi.Input[str] = "error.html"):
        self.index_document = index_document
        self.error_document = error_document

class StaticSite(pulumi.ComponentResource):
    bucket_name: pulumi.Output[str]
    website_url: pulumi.Output[str]

    def __init__(self, name: str, args: StaticSiteArgs,
                 opts: pulumi.ResourceOptions = None):
        super().__init__("myorg:index:StaticSite", name, None, opts)

        bucket = aws.s3.Bucket(f"{name}-bucket",
            opts=pulumi.ResourceOptions(parent=self))

        website = aws.s3.BucketWebsiteConfigurationV2(f"{name}-website",
            bucket=bucket.id,
            index_document=aws.s3.BucketWebsiteConfigurationV2IndexDocumentArgs(
                suffix=args.index_document,
            ),
            error_document=aws.s3.BucketWebsiteConfigurationV2ErrorDocumentArgs(
                key=args.error_document,
            ),
            opts=pulumi.ResourceOptions(parent=self))

        self.bucket_name = bucket.id
        self.website_url = website.website_endpoint

        self.register_outputs({
            "bucket_name": self.bucket_name,
            "website_url": self.website_url,
        })

site = StaticSite("marketing", StaticSiteArgs())
pulumi.export("url", site.website_url)
型 URN の形式

super() の最初の引数は型 URN です:<package>:<module>:<type>。

セグメント規則例
package組織名またはパッケージ名myorg、acme、pkg
module通常は indexindex
typePascalCase のクラス名StaticSite、VpcNetwork

完全な例:myorg:index:StaticSite、acme:index:KubernetesCluster

registerOutputs は必須です

理由:registerOutputs() がないと、Pulumi コンソールでコンポーネントが「作成中」の状態のままになり、出力が状態に保存されません。

誤り:

class MyComponent extends pulumi.ComponentResource {
    public readonly url: pulumi.Output<string>;

    constructor(name: string, args: MyArgs, opts?: pulumi.ComponentResourceOptions) {
        super("myorg:index:MyComponent", name, {}, opts);
        const bucket = new aws.s3.Bucket(`${name}-bucket`, {}, { parent: this });
        this.url = bucket.bucketRegionalDomainName;
        // Missing registerOutputs -- component stuck "creating"
    }
}

正しい例:

class MyComponent extends pulumi.ComponentResource {
    public readonly url: pulumi.Output<string>;

    constructor(name: string, args: MyArgs, opts?: pulumi.ComponentResourceOptions) {
        super("myorg:index:MyComponent", name, {}, opts);
        const bucket = new aws.s3.Bucket(`${name}-bucket`, {}, { parent: this });
        this.url = bucket.bucketRegionalDomainName;

        this.registerOutputs({ url: this.url });
    }
}
子リソース名はコンポーネント名から派生させる

理由:子リソース名をハードコードすると、コンポーネントを複数回インスタンス化したときに名前が衝突します。

誤り:

// Collides if two instances of this component exist
const bucket = new aws.s3.Bucket("my-bucket", {}, { parent: this });

正しい例:

// Unique per component instance
const bucket = new aws.s3.Bucket(`${name}-bucket`, {}, { parent: this });

Args インターフェースの設計

args インターフェースは、設計上最も大きな影響を及ぼす要素です。利用者が設定できる内容と、コンポーネントの組み合わせやすさを決定します。

プロパティを Input でラップする

理由:Input<T> は通常の値と、ほかのリソースからの Output<T> の両方を受け取ります。これを使わないと、利用者は .apply() を使って手動で出力をアンラップする必要があります。

誤り:

interface WebServiceArgs {
    port: number;            // Forces consumers to unwrap Outputs
    vpcId: string;           // Cannot accept vpc.id directly
}

正しい例:

interface WebServiceArgs {
    port: pulumi.Input<number>;     // Accepts 8080 or someOutput
    vpcId: pulumi.Input<string>;    // Accepts "vpc-123" or vpc.id
}
構造をフラットに保つ

深くネストした args オブジェクトは避けてください。フラットなインターフェースは使いやすく、変更にも対応しやすくなります。

// Prefer flat
interface DatabaseArgs {
    instanceClass: pulumi.Input<string>;
    storageGb: pulumi.Input<number>;
    enableBackups?: pulumi.Input<boolean>;
    backupRetentionDays?: pulumi.Input<number>;
}

// Avoid deep nesting
interface DatabaseArgs {
    instance: {
        compute: { class: pulumi.Input<string> };
        storage: { sizeGb: pulumi.Input<number> };
    };
    backup: {
        config: { enabled: pulumi.Input<boolean>; retention: pulumi.Input<number> };
    };
}
Union 型は使用しない

Union 型は複数言語向けの SDK 生成を妨げます。Python、Go、C# では string | number を表現できません。

誤り:

interface MyArgs {
    port: pulumi.Input<string | number>;  // Fails in Python, Go, C#
}

正しい例:

interface MyArgs {
    port: pulumi.Input<number>;  // Single type, works everywhere
}

複数の形式を受け入れる必要がある場合は、個別のオプショナルプロパティを使用してください。

interface StorageArgs {
    sizeGb?: pulumi.Input<number>;      // Specify size in GB
    sizeMb?: pulumi.Input<number>;      // Or specify size in MB
}
関数やコールバックは使用しない

関数は言語の境界を越えてシリアライズできません。

誤り:

interface MyArgs {
    nameTransform: (name: string) => string;  // Cannot serialize
}

正しい例:

interface MyArgs {
    namePrefix?: pulumi.Input<string>;   // Configuration instead of callback
    nameSuffix?: pulumi.Input<string>;
}
オプショナルプロパティにはデフォルト値を設定する

利用者が必要な設定だけを行えるように、コンストラクター内で妥当なデフォルト値を設定します。

interface SecureBucketArgs {
    enableVersioning?: pulumi.Input<boolean>;   // Defaults to true
    enableEncryption?: pulumi.Input<boolean>;   // Defaults to true
    blockPublicAccess?: pulumi.Input<boolean>;  // Defaults to true
}

class SecureBucket extends pulumi.ComponentResource {
    constructor(name: string, args: SecureBucketArgs, opts?: pulumi.ComponentResourceOptions) {
        super("myorg:index:SecureBucket", name, {}, opts);

        const enableVersioning = args.enableVersioning ?? true;
        const enableEncryption = args.enableEncryption ?? true;
        const blockPublicAccess = args.blockPublicAccess ?? true;

        // Apply defaults...
    }
}

// Consumer only overrides what they need
const bucket = new SecureBucket("data", { enableVersioning: false });

出力の公開

利用者が必要とするものだけを公開する

コンポーネントは多くの内部リソースを作成することがよくあります。すべての内部リソースではなく、利用者が必要とする値だけを公開してください。

誤り:

class Database extends pulumi.ComponentResource {
    // Exposes everything -- consumers see implementation details
    public readonly cluster: aws.rds.Cluster;
    public readonly primaryInstance: aws.rds.ClusterInstance;
    public readonly replicaInstance: aws.rds.ClusterInstance;
    public readonly subnetGroup: aws.rds.SubnetGroup;
    public readonly securityGroup: aws.ec2.SecurityGroup;
    public readonly parameterGroup: aws.rds.ClusterParameterGroup;
    // ...
}

正しい例:

class Database extends pulumi.ComponentResource {
    // Exposes only what consumers need
    public readonly endpoint: pulumi.Output<string>;
    public readonly port: pulumi.Output<number>;
    public readonly securityGroupId: pulumi.Output<string>;

    constructor(name: string, args: DatabaseArgs, opts?: pulumi.ComponentResourceOptions) {
        super("myorg:index:Database", name, {}, opts);

        const sg = new aws.ec2.SecurityGroup(`${name}-sg`, { /* ... */ }, { parent: this });
        const cluster = new aws.rds.Cluster(`${name}-cluster`, { /* ... */ }, { parent: this });

        this.endpoint = cluster.endpoint;
        this.port = cluster.port;
        this.securityGroupId = sg.id;

        this.registerOutputs({
            endpoint: this.endpoint,
            port: this.port,
            securityGroupId: this.securityGroupId,
        });
    }
}
複合出力を導出する

pulumi.interpolate または pulumi.concat を使用して、導出した値を作成します。

this.connectionString = pulumi.interpolate`postgresql://${args.username}:${args.password}@${cluster.endpoint}:${cluster.port}/${args.databaseName}`;

this.registerOutputs({ connectionString: this.connectionString });

コンポーネントの設計パターン

妥当なデフォルト値と上書き

ベストプラクティスをデフォルト値として組み込みます。利用者に固有の要件がある場合は、上書きできるようにします。

interface SecureBucketArgs {
    enableVersioning?: pulumi.Input<boolean>;
    enableEncryption?: pulumi.Input<boolean>;
    blockPublicAccess?: pulumi.Input<boolean>;
    tags?: pulumi.Input<Record<string, pulumi.Input<string>>>;
}

class SecureBucket extends pulumi.ComponentResource {
    public readonly bucketId: pulumi.Output<string>;
    public readonly arn: pulumi.Output<string>;

    constructor(name: string, args: SecureBucketArgs = {}, opts?: pulumi.ComponentResourceOptions) {
        super("myorg:index:SecureBucket", name, {}, opts);

        const bucket = new aws.s3.Bucket(`${name}-bucket`, {
            tags: args.tags,
        }, { parent: this });

        // Versioning on by default
        if (args.enableVersioning !== false) {
            new aws.s3.BucketVersioningV2(`${name}-versioning`, {
                bucket: bucket.id,
                versioningConfiguration: { status: "Enabled" },
            }, { parent: this });
        }

        // Encryption on by default
        if (args.enableEncryption !== false) {
            new aws.s3.BucketServerSideEncryptionConfigurationV2(`${name}-encryption`, {
                bucket: bucket.id,
                rules: [{ applyServerSideEncryptionByDefault: { sseAlgorithm: "AES256" } }],
            }, { parent: this });
        }

        // Public access blocked by default
        if (args.blockPublicAccess !== false) {
            new aws.s3.BucketPublicAccessBlock(`${name}-public-access`, {
                bucket: bucket.id,
                blockPublicAcls: true,
                blockPublicPolicy: true,
                ignorePublicAcls: true,
                restrictPublicBuckets: true,
            }, { parent: this });
        }

        this.bucketId = bucket.id;
        this.arn = bucket.arn;
        this.registerOutputs({ bucketId: this.bucketId, arn: this.arn });
    }
}
条件付きリソース作成

オプショナルな args を使って、サブリソースの作成を制御します。

interface WebServiceArgs {
    image: pulumi.Input<string>;
    port: pulumi.Input<number>;
    enableMonitoring?: pulumi.Input<boolean>;
    alarmEmail?: pulumi.Input<string>;
}

class WebService extends pulumi.ComponentResource {
    constructor(name: string, args: WebServiceArgs, opts?: pulumi.ComponentResourceOptions) {
        super("myorg:index:WebService", name, {}, opts);

        const service = new aws.ecs.Service(`${name}-service`, {
            // ...service config...
        }, { parent: this });

        // Only create alarm infrastructure when monitoring is enabled
        if (args.enableMonitoring) {
            const topic = new aws.sns.Topic(`${name}-alerts`, {}, { parent: this });

            if (args.alarmEmail) {
                new aws.sns.TopicSubscription(`${name}-alert-email`, {
                    topic: topic.arn,
                    protocol: "email",
                    endpoint: args.alarmEmail,
                }, { parent: this });
            }

            new aws.cloudwatch.MetricAlarm(`${name}-cpu-alarm`, {
                // ...alarm config referencing service...
                alarmActions: [topic.arn],
            }, { parent: this });
        }

        this.registerOutputs({});
    }
}
合成

低レベルのコンポーネントから、より高レベルのコンポーネントを構築します。各レベルは単一の関心事を管理します。

// Lower-level component
class VpcNetwork extends pulumi.ComponentResource {
    public readonly vpcId: pulumi.Output<string>;
    public readonly publicSubnetIds: pulumi.Output<string>[];
    public readonly privateSubnetIds: pulumi.Output<string>[];

    constructor(name: string, args: VpcNetworkArgs, opts?: pulumi.ComponentResourceOptions) {
        super("myorg:index:VpcNetwork", name, {}, opts);
        // ...create VPC, subnets, route tables...
        this.registerOutputs({ vpcId: this.vpcId });
    }
}

// Higher-level component that uses VpcNetwork
class Platform extends pulumi.ComponentResource {
    public readonly kubeconfig: pulumi.Output<string>;

    constructor(name: string, args: PlatformArgs, opts?: pulumi.ComponentResourceOptions) {
        super("myorg:index:Platform", name, {}, opts);

        // Compose lower-level components
        const network = new VpcNetwork(`${name}-network`, {
            cidrBlock: args.cidrBlock,
        }, { parent: this });

        const cluster = new aws.eks.Cluster(`${name}-cluster`, {
            vpcConfig: {
                subnetIds: network.privateSubnetIds,
            },
        }, { parent: this });

        this.kubeconfig = cluster.kubeconfig;
        this.registerOutputs({ kubeconfig: this.kubeconfig });
    }
}
プロバイダーの引き渡し

マルチリージョンまたはマルチアカウントでのデプロイでは、プロバイダーを明示的に指定できます。ComponentResourceOptions はプロバイダー設定を子リソースに自動的に引き継ぎます。

// Consumer passes a provider for a different region
const usWest = new aws.Provider("us-west", { region: "us-west-2" });
const site = new StaticSite("west-site", { indexDocument: "index.html" }, {
    providers: [usWest],
});

{ parent: this } を指定した子リソースは、プロバイダーを自動的に継承します。コンポーネント内に追加のコードは必要ありません。


マルチ言語コンポーネント

コンポーネントが複数の Pulumi 言語(TypeScript、Python、Go、C#、Java、YAML)から利用される場合は、マルチ言語コンポーネントとしてパッケージ化します。

マルチ言語対応は必要ですか?

「このコンポーネントを、作成時とは異なる言語から利用する人はいますか?」と考えてみてください。

単一言語コンポーネント(パッケージ化は不要):

  • チームが1つの言語を使用し、コンポーネントがそのコードベース内にとどまる
  • コンポーネントが単一のプロジェクトまたはモノレポ内でのみ使用される
  • PulumiPlugin.yaml は不要です。クラスを直接インポートするだけです

マルチ言語コンポーネント(パッケージ化が必要):

  • 他のチームが異なる言語でコンポーネントを利用する
  • 開発者が使用する言語を選べるように、プラットフォームチームが抽象化を構築する
  • YAML の利用者がアクセスする必要がある。TypeScript で作成した場合でも、YAML プログラムからコンポーネントを使うにはマルチ言語パッケージ化が必要です
  • 組織内で共有するコンポーネントライブラリを構築する
  • Pulumi のプライベートレジストリまたはパブリックレジストリへの公開は一般的な理由ですが、マルチ言語対応に必須ではありません

よくある間違い:TypeScript のプラットフォームチームが、TypeScript の利用者だけが使えるコンポーネントを構築してしまいます。アプリケーション開発者が Python または YAML を使う場合、マルチ言語パッケージ化をしないと、これらのコンポーネントを利用できません。

セットアップ

コンポーネントディレクトリに PulumiPlugin.yaml を作成し、ランタイムを宣言します。

runtime: nodejs

Python の場合:

runtime: python
シリアライズの制約

マルチ言語での互換性を確保するには、引数がシリアライズ可能である必要があります。これらの制約は作成時の言語にかかわらず適用されます。

使用可能使用不可
string、number、booleanユニオン型(string | number)
Input<T> ラッパー関数とコールバック
プリミティブ型の配列とマップ複雑なネストされたジェネリクス
列挙型プラットフォーム固有の型
マルチ言語コンポーネントの利用

利用者は pulumi package add を使ってコンポーネントをインストールします。これにより、プロバイダープラグインが自動的にダウンロードされ、利用者の言語に対応するローカル SDK が生成され、Pulumi.yaml が更新されます。

# From a Git repository
pulumi package add <git-repo-url>

# From a specific version tag
pulumi package add <git-repo-url>@v1.0.0

新規チェックアウトや CI 環境では、pulumi install を実行して、すべてのパッケージ依存関係が利用可能であることを確認してください。利用者が SDK を手動で生成する必要はありません。

SDK をパッケージマネージャー(npm、PyPI など)に公開する作成者は、任意で pulumi package gen-sdk を使って、公開用の言語固有 SDK を生成できます。ほとんどのコンポーネント作成者には不要です。pulumi package add が利用者側での SDK 生成を処理します。

エントリーポイント

公開済みのマルチ言語コンポーネントには、コンポーネントプロバイダープロセスをホストするエントリーポイントが必要です。エントリーポイントのパターンは言語によって異なります。

TypeScript(runtime: nodejs):

index.ts からコンポーネントクラスをエクスポートします。個別のエントリーポイントファイルは不要です。Pulumi がエクスポートされたクラスを自動的に調べます。

// index.ts -- exports are the entry point
export { StaticSite, StaticSiteArgs } from "./staticSite";
export { SecureBucket, SecureBucketArgs } from "./secureBucket";

Python(runtime: python):

すべてのコンポーネントクラスを指定して component_provider_host を呼び出す __main__.py を作成します。

from pulumi.provider.experimental import component_provider_host
from static_site import StaticSite
from secure_bucket import SecureBucket

if __name__ == "__main__":
    component_provider_host(
        name="my-components",
        components=[StaticSite, SecureBucket],
    )

Go(runtime: go):

プロバイダーを構築して実行する main.go を作成します。

package main

import (
    "context"
    "fmt"
    "os"

    "github.com/pulumi/pulumi-go-provider/infer"
)

func main() {
    p, err := infer.NewProviderBuilder().
        WithComponents(
            infer.ComponentF(NewStaticSite),
            infer.ComponentF(NewSecureBucket),
        ).
        Build()
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    }
    if err := p.Run(context.Background(), "my-components", "0.1.0"); err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    }
}

C#(runtime: dotnet):

コンポーネントプロバイダーホストを提供する Program.cs を作成します。

using System.Threading.Tasks;

class Program
{
    public static Task Main(string[] args) =>
        Pulumi.Experimental.Provider.ComponentProviderHost.Serve(args);
}

すべての言語に対応した完全な動作例については、https://github.com/mikhailshilkov/comp-as-comp をご覧ください。

参考資料:https://www.pulumi.com/docs/iac/using-pulumi/pulumi-packages/


配布

対象ユーザーに応じて配布方法を選択します。

対象ユーザー方法手順
同じプロジェクト直接インポート標準的な言語のインポート
同じ組織プライベートレジストリpulumi package publish で Pulumi Cloud に公開
同じ組織Git リポジトリバージョンタグを付けて pulumi package add <repo>
言語エコシステムパッケージマネージャーnpm、PyPI、NuGet、または Maven に公開
パブリックコミュニティPulumi Registrypulumi/registry GitHub リポジトリから申請
Pulumi Private Registry

プライベートレジストリは、組織のコンポーネントを一元管理するカタログです。すべてのチームに対して、API ドキュメントの自動生成、バージョン管理、検索性を提供します。

コンポーネントをプライベートレジストリに公開します。

pulumi package publish https://github.com/myorg/my-component --publisher myorg

v プレフィックスを付けた Git タグを使用してコンポーネントをバージョン管理します。

git tag v1.0.0
git push origin v1.0.0

公開時には README ファイルが必要です。Pulumi はこれをレジストリ内のコンポーネントのドキュメントページとして使用します。

OIDC 認証を使用し、GitHub Actions からの公開を自動化します。

name: Publish Component
on:
  push:
    tags:
      - "v*"

permissions:
  id-token: write
  contents: read

jobs:
  publish:
    runs-on: ubuntu-latest
    env:
      PULUMI_ORG: myorg
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - uses: pulumi/auth-actions@v1
        with:
          organization: ${{ env.PULUMI_ORG }}
          requested-token-type: urn:pulumi:token-type:access_token:organization
      - run: pulumi package publish https://github.com/${{ github.repository }} --publisher ${{ env.PULUMI_ORG }}

前提条件:このワークフローを使用する前に、Pulumi Cloud で GitHub OIDC インテグレーションを設定してください。

レジストリは、プライベートな GitHub リポジトリと GitLab リポジトリをサポートしています。OIDC を使用しない構成では、環境変数 GITHUB_TOKEN または GITLAB_TOKEN で認証してください。

プライベートレジストリは、公開されたコンポーネントごとに SDK ドキュメントを自動生成します。コンポーネントの入力と出力に型注釈を追加して、生成されたドキュメントを充実させてください(TypeScript では JSDoc、Python では docstring、Go では Annotate() メソッドを使用します)。

リファレンス:https://www.pulumi.com/docs/idp/get-started/private-registry/

Git リポジトリによる配布

利用者がバージョンを固定できるよう、リリースにタグを付けます。

git tag v1.0.0
git push origin v1.0.0

利用者は次のコマンドでインストールします。

pulumi package add https://github.com/myorg/[email protected]
パッケージマネージャーによる配布

言語固有のパッケージを公開し、ネイティブの依存関係管理を利用できるようにします。

  • npm:TypeScript/JavaScript では npm publish
  • PyPI:Python では twine upload
  • NuGet:.NET では dotnet nuget push
  • Maven Central:Java では標準的な Maven の公開手順

リファレンス:https://www.pulumi.com/docs/iac/using-pulumi/pulumi-packages/


アンチパターン

アンチパターン問題修正方法
apply() 内のリソースpulumi preview に表示されないリソースの作成を apply の外に移します(pulumi-best-practices のプラクティス 1 を参照)
registerOutputs() の不足コンポーネントが「作成中」のままになるコンストラクターの最後の行で必ず呼び出します
parent: this の不足子リソースがルートレベルに表示されるすべての子リソースに { parent: this } を渡します
args 内の共用体型Python、Go、C# の SDK で問題が発生する単一の型を使用し、バリエーションごとにプロパティを分けます
args 内の関数言語間でシリアライズできない代わりに構成プロパティを使用します
子リソース名のハードコード複数のインスタンスで名前が衝突する${name}-suffix から名前を生成します
過剰な出力の公開実装の詳細が漏れる利用者が必要とするものだけをエクスポートします
一度しか使わないコンポーネント不必要な抽象化のオーバーヘッドパターンが繰り返されるまではインラインリソースを使用します
過度にネストされた args使いにくく、変更もしにくいインターフェースは任意プロパティを使ってフラットに保ちます

クイックリファレンス

トピック要点
型 URN<package>:<module>:<type>。モジュールは通常 index
コンストラクターsuper(type, name, {}, opts) の後に子リソースを作成し、registerOutputs() を呼び出す
子リソース常に { parent: this } を指定し、${name}-suffix から名前を生成する
Args インターフェースInput<T> でラップし、共用体型と関数は使わず、フラットな構造にする
出力公開する readonly Output<T> プロパティ。必要なものだけを公開する
デフォルト値コンストラクター内で適切なデフォルト値を設定するには ?? 演算子を使う
合成低レベルコンポーネントを高レベルコンポーネントに合成する
マルチランゲージPulumiPlugin.yaml とエントリーポイントを用意する。利用者は pulumi package add を使用する
配布プライベートレジストリ、Git タグ、パッケージマネージャー、またはパブリックな Pulumi Registry

関連スキル

  • pulumi-best-practices:Output の処理、シークレット、エイリアスなど、Pulumi の一般的なパターン
  • pulumi-automation-api:統合テストや複数スタックのワークフロー向けのプログラムによるオーケストレーション
  • pulumi-esc:コンポーネントのデプロイ向けの一元化されたシークレットと構成

参考資料

コメント