Pulumi Infrastruktur als Code
Kurzanleitung: Definieren Sie Cloud-Infrastruktur in TypeScript mit vollständiger Typsicherheit. Verwenden Sie
ComponentResource, um wiederverwendbare Infrastrukturmuster zu kapseln. Übergeben Sie{ parent: this }an alle untergeordneten Ressourcen innerhalb von Komponenten. Verwenden Siepulumi.interpolatefür die Zeichenkettenbildung mit Outputs (nicht Zeichenkettenverkettung). Erstellen Sie niemals Ressourcen innerhalb von.apply(). Verwenden SieConfig.requireSecret()für sensible Werte. Bevorzugen Sietransformsgegenüber veraltetentransformations. Rufen Sie immerthis.registerOutputs()am Ende von Komponentenkonstruktoren auf.
<critical_requirements>
KRITISCH: Vor der Verwendung dieses Skills
Der gesamte Code muss den Projektkonventionen in CLAUDE.md folgen (kebab-case, benannte Exporte, Import-Reihenfolge,
import type, benannte Konstanten)
(Sie MÜSSEN { parent: this } an ALLE untergeordneten Ressourcen innerhalb einer ComponentResource übergeben – das Weglassen unterbricht den Ressourcenbaum und die Zustandsverfolgung)
(Sie MÜSSEN pulumi.interpolate für die Zeichenkettenbildung mit Outputs verwenden – Zeichenkettenverkettung erzeugt stillschweigend [object Object])
(Sie DÜRFEN NIEMALS Ressourcen innerhalb von .apply() erstellen – sie erscheinen nicht in pulumi preview und verursachen Reihenfolgeprobleme)
(Sie MÜSSEN Config.requireSecret() für sensible Werte verwenden – Config.require() speichert Werte als Klartext im Zustand)
(Sie MÜSSEN this.registerOutputs() am Ende jedes ComponentResource-Konstruktors aufrufen – das Weglassen verhindert die Ausgabeverfolgung)
</critical_requirements>
Detaillierte Ressourcen:
- examples/core.md – Ressourcendefinitionen, Komponentenressourcen, Benennung, Outputs, Konfiguration/Geheimnisse
- examples/advanced.md – Stack-Referenzen, Transforms, dynamische Anbieter, Automation API, Policy Packs
- reference.md – Entscheidungsrahmen, Ressourcenoptionstabelle, API-Referenz, CLI-Befehle
Auto-Erkennung: Pulumi, @pulumi/pulumi, @pulumi/aws, @pulumi/gcp, @pulumi/azure, @pulumi/kubernetes, pulumi.ComponentResource, pulumi.CustomResource, pulumi.Output, pulumi.Config, pulumi.interpolate, pulumi.all, registerOutputs, StackReference, ComponentResourceOptions, CustomResourceOptions, dynamic.Resource, dynamic.ResourceProvider, LocalWorkspace, InlineProgramArgs, Automation API, CrossGuard, PolicyPack
Wann zu verwenden:
- Definieren von Cloud-Infrastruktur in TypeScript mit typsicheren Ressourcen-APIs
- Erstellen wiederverwendbarer Infrastrukturkomponenten mit
ComponentResource - Verwalten von Multi-Stack-Architekturen mit Stack-Referenzen
- Umgang mit Geheimnissen und umgebungsspezifischer Konfiguration
- Erstellen von Self-Service-Infrastrukturplattformen mit der Automation API
- Schreiben von Compliance-Richtlinien mit CrossGuard Policy Packs
Wann NICHT zu verwenden:
- Einmalige Shell-Skripte, die eine einzelne Ressource erstellen (verwenden Sie direkt die Cloud-CLI)
- Projekte, bei denen das Team keine TypeScript-Erfahrung hat (erwägen Sie andere IaC-Sprachoptionen)
Wichtige behandelte Muster:
- Ressourcendefinitionen mit typisierten Eingaben und automatischer Benennung
- ComponentResource-Kapselung (parent, Benennung, registerOutputs)
- Outputs:
apply,all,interpolateund Lifting - Konfiguration und Geheimnisverwaltung (
Config.require,Config.requireSecret,pulumi.secret) - Stack-Referenzen für stackübergreifenden Datenaustausch
- Ressourcenoptionen (
dependsOn,protect,aliases,ignoreChanges,transforms) - Dynamische Anbieter für benutzerdefinierte CRUD-Ressourcen
- Automation API für programmatisches Stack-Management
- CrossGuard Policy Packs für Compliance-Durchsetzung
Philosophie
Pulumi behandelt Infrastruktur als echten Code, nicht als Konfigurationsdateien. TypeScript bietet Typsicherheit, IDE-Autovervollständigung, Refactoring-Tools und das gesamte Node.js-Ökosystem. Ressourcen sind Objekte, Abhängigkeiten sind automatisch, und Wiederverwendung erfolgt durch Funktionen und Klassen – nicht durch eine benutzerdefinierte Modulsprache.
Kernprinzipien:
- Ressourcen sind Objekte: Jede Cloud-Ressource ist eine TypeScript-Klasseninstanz mit typisierten Eingaben und Ausgaben
- Abhängigkeiten sind automatisch: Wenn Sie die Ausgabe einer Ressource als Eingabe einer anderen übergeben, leitet Pulumi den Abhängigkeitsgraphen ab
- Wiederverwendung durch Komponenten:
ComponentResourcekapselt mehrere Ressourcen in einer einzigen logischen Einheit mit eigenen Ein- und Ausgaben - Outputs sind Promises:
Output<T>repräsentiert einen Wert, der erst nach der Bereitstellung bekannt sein kann – verwenden Sieapply,alloderinterpolate, um damit zu arbeiten, entpacken Sie niemals manuell - Zustand wird verwaltet: Pulumi verfolgt jede Ressource im Zustand – das Ändern eines logischen Namens oder das Verschieben einer Ressource zwischen Dateien löst ein Löschen und Neuerstellen aus, es sei denn, Sie verwenden
aliases
Kernmuster
Muster 1: Ressourcendefinitionen
Jede Ressource benötigt einen logischen Namen (für Zustandsverfolgung), ein Argumentenbündel (typisierte Eingaben) und optionale Ressourcenoptionen.
const bucket = new aws.s3.Bucket(
"data-bucket",
{
versioning: { enabled: true },
lifecycleRules: [
{ enabled: true, expiration: { days: BUCKET_EXPIRY_DAYS } },
],
},
{ protect: true },
); // Versehentliches Löschen verhindern
Wichtige Punkte: Logische Namen müssen pro Typ innerhalb eines Stacks eindeutig sein. Pulumi hängt automatisch ein zufälliges Suffix an den physischen Namen an, um Kollisionen zu vermeiden. Verwenden Sie protect: true bei kritischen Ressourcen. Exportieren Sie Outputs für die stackübergreifende Nutzung.
Siehe examples/core.md für Ressourcenbenennung, automatische Benennungskonfiguration und Anbieteroptionen.
Muster 2: ComponentResource-Kapselung
Packen Sie verwandte Ressourcen in eine ComponentResource, um wiederverwendbare Infrastruktureinheiten zu erstellen.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
interface StaticSiteArgs {
indexDocument?: string;
errorDocument?: 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,
) {
super("myinfra:web:StaticSite", name, args, opts);
const bucket = new aws.s3.BucketV2(`${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 },
);
this.bucketName = bucket.id;
this.websiteUrl = website.websiteEndpoint;
this.registerOutputs({
bucketName: this.bucketName,
websiteUrl: this.websiteUrl,
});
}
}
Warum gut: untergeordnete Ressourcen verwenden { parent: this }, der Name wird vom übergeordneten Element abgeleitet, registerOutputs wird aufgerufen, der Typ-Token folgt dem Format pkg:module:Type
Siehe examples/core.md für vollständige Komponentenmuster mit Anbietervererbung und Multi-Ressourcen-Komponenten.
Muster 3: Arbeiten mit Outputs
Outputs repräsentieren Werte, die nach der Bereitstellung aufgelöst werden. Verwenden Sie niemals Zeichenkettenverkettung – verwenden Sie interpolate, apply oder all.
// interpolate – getaggtes Template für Zeichenkettenbildung (bevorzugt)
const url = pulumi.interpolate`https://${bucket.bucketRegionalDomainName}/index.html`;
// apply – transformiert einen einzelnen Output
const upper = bucket.id.apply((id) => id.toUpperCase());
// all – kombiniert mehrere Outputs
const endpoint = pulumi
.all([lb.dnsName, listener.port])
.apply(([dns, port]) => `http://${dns}:${port}`);
// Lifting – direkt auf Eigenschaften von Ressourcenoutputs zugreifen
const subnetId = vpc.subnets[0].id; // Kein apply für bekannte Eigenschaften erforderlich
Warum gut: interpolate behandelt Output-Werte transparent, all wartet auf mehrere Werte, Lifting vermeidet unnötige apply-Aufrufe
Problemzone: Ressourcen, die innerhalb von .apply() erstellt werden, erscheinen nicht in pulumi preview und können Reihenfolgeprobleme verursachen. Übergeben Sie Outputs immer direkt als Eingaben an andere Ressourcen.
Siehe examples/core.md für Output-Muster, pulumi.output()-Wrapping und das Apply-Anti-Pattern.
Muster 4: Konfiguration und Geheimnisse
Verwenden Sie pulumi.Config für stackspezifische Werte. Verwenden Sie requireSecret für sensible Daten – es verschlüsselt den Wert im Zustand.
const config = new pulumi.Config();
// Klartext-Konfigurationswerte
const region = config.require("region"); // Schlägt fehl, wenn nicht vorhanden
const nodeCount = config.getNumber("nodeCount"); // Gibt undefined zurück, wenn nicht vorhanden
// Geheime Werte – im Zustand verschlüsselt
const dbPassword = config.requireSecret("dbPassword");
const apiKey = config.getSecret("apiKey");
// Programmatische Werte als geheim markieren
const connectionString = pulumi.interpolate`postgres://admin:${dbPassword}@${db.endpoint}/mydb`;
// connectionString ist automatisch geheim, weil dbPassword geheim ist
// Wert explizit als geheim markieren
const token = pulumi.secret(generateToken());
Wichtiger Punkt: Jeder Output, der von einem Geheimnis abgeleitet ist, wird automatisch als geheim markiert. Sie müssen abgeleitete Werte nicht erneut markieren.
Siehe examples/core.md für namensraumbezogene Konfiguration, geheime Outputs und CLI-Befehle zum Setzen der Konfiguration.
Muster 5: Ressourcenoptionen
Ressourcenoptionen steuern das Lebenszyklusverhalten. Die wichtigsten sind:
const db = new aws.rds.Instance(
"primary-db",
{
/* ... */
},
{
protect: true, // Versehentliches Löschen verhindern
dependsOn: [vpc, securityGroup], // Explizite Reihenfolge
ignoreChanges: ["tags"], // Drift bei bestimmten Eigenschaften ignorieren
aliases: [{ name: "old-db-name" }], // Umbenennen ohne Neuerstellung
retainOnDelete: true, // Cloud-Ressource bei pulumi destroy behalten
deleteBeforeReplace: true, // Für Ressourcen, die eindeutig sein müssen
replaceOnChanges: ["engine"], // Erzwingt Austausch bei bestimmten Änderungen
provider: usEastProvider, // Expliziter Anbieter (Region, Konto)
},
);
Problemzone: Das Ändern des logischen Namens oder der übergeordneten Ressource einer Ressource führt dazu, dass Pulumi sie löscht und neu erstellt. Verwenden Sie aliases, um sicher umzubenennen.
Siehe reference.md für die vollständige Tabelle der Ressourcenoptionen.
Muster 6: Stack-Referenzen
Teilen Sie Outputs zwischen Stacks mit StackReference.
// Im Netzwerk-Stack: Outputs exportieren
export const vpcId = vpc.id;
export const subnetIds = subnets.map((s) => s.id);
// Im Anwendungs-Stack: Outputs konsumieren
const networkStack = new pulumi.StackReference("myorg/networking/prod");
const vpcId = networkStack.getOutput("vpcId");
const subnetIds = networkStack.getOutput("subnetIds");
// requireOutput schlägt fehl, wenn der Output nicht existiert (sicherer als getOutput)
const vpcIdRequired = networkStack.requireOutput("vpcId");
Siehe examples/advanced.md für Stack-Referenz-Muster und getOutputDetails.
Muster 7: Transforms
Wenden Sie Transformationen auf Ressourcen und ihre untergeordneten Elemente an. Verwenden Sie transforms (nicht die veralteten transformations). Transforms erhalten ein Argumentobjekt mit type, props und opts und geben ein modifiziertes Ergebnis oder undefined zurück, um zu überspringen.
// Auf Ressourcenebene: Tags auf alle tagbaren untergeordneten Elemente einer Komponente anwenden
const vpc = new MyVpcComponent(
"vpc",
{},
{
transforms: [
(args) => {
if (isTaggable(args.type)) {
return {
props: {
...args.props,
tags: { ...args.props["tags"], ManagedBy: "pulumi" },
},
opts: args.opts,
};
}
return undefined;
},
],
},
);
Wichtiger Unterschied zu veralteten transformations: transforms unterstützen das Modifizieren von gepackten Komponenten-Unterelementen (awsx, eks), unterstützen asynchrone Callbacks und übergeben kein Resource-Objekt.
Siehe examples/advanced.md für Stack-Level-Transforms, Optionsmodifikation und Migration von transformations.
Muster 8: Automation API
Führen Sie Pulumi programmatisch ohne CLI aus – für Self-Service-Plattformen, Integrationstests oder benutzerdefinierte Bereitstellungstools.
const stack = await LocalWorkspace.createOrSelectStack({
stackName: "dev",
projectName: "my-platform",
program: async () => {
const bucket = new aws.s3.Bucket("auto-bucket");
return { bucketName: bucket.id };
},
});
const upResult = await stack.up({ onOutput: console.log });
Wichtiger Punkt: Die Automation API erfordert, dass die Pulumi-CLI installiert und im PATH ist – sie verwendet die Engine der CLI im Hintergrund.
Siehe examples/advanced.md für Preview, Destroy, lokalen Programmmodus, Konfigurationseinrichtung und Stack-Output-Abruf.
<red_flags>
ROTE FLAGGEN
Hohe Priorität:
- Ressourcen innerhalb von
.apply()erstellen – Sie erscheinen nicht inpulumi preview, verursachen Reihenfolgeprobleme und unterbrechen den Abhängigkeitsgraph. Übergeben Sie Outputs direkt als Ressourceneingaben. - Fehlendes
{ parent: this }in ComponentResource-Unterelementen – Ressourcen erscheinen auf der Wurzelebene des Zustandsbaums und unterbrechen die logische Gruppierung und das kaskadierende Löschen von Komponenten. - Zeichenkettenverkettung mit Outputs –
"https://" + bucket.iderzeugt[object Object]. Verwenden Sie stattdessenpulumi.interpolate. - Verwenden von
Config.require()für Passwörter/Schlüssel – Speichert den Wert als Klartext im Zustand. Verwenden SieConfig.requireSecret(), um zu verschlüsseln. - Vergessen von
this.registerOutputs()– Komponenten-Outputs werden nicht ordnungsgemäß im Zustand verfolgt oder über Stack-Referenzen verfügbar sein. - Ändern logischer Namen ohne Aliase – Pulumi löscht und erstellt die Ressource neu. Verwenden Sie
aliases: [{ name: "old-name" }], um sicher umzubenennen.
Mittlere Priorität:
- Verlassen auf Standardanbieter in Multi-Region-Setups – Verwenden Sie explizite Anbieter pro Region. Setzen Sie
pulumi:disable-default-providersin der Konfiguration, um dies zu erzwingen. - Mit Funktionen statt ComponentResource beginnen – Eine spätere Migration erfordert Aliase oder Ressourcenneuerstellung. Verwenden Sie von Anfang an Komponenten.
dependsOnnicht für nicht offensichtliche Abhängigkeiten verwenden – Pulumi leitet Abhängigkeiten aus der Input/Output-Verdrahtung ab, aber Seiteneffekte (IAM-Ausbreitung, DNS-Lösung) benötigen möglicherweise explizitesdependsOn.
Niedrige Priorität:
pulumi.all([...])anstelle vonpulumi.all([...]).apply(([...]) => ...)–allmuss mitapplyabgeschlossen werden, um einen tatsächlichen Wert zu erhalten.
</red_flags>