¿Qué hace Pulumi Infraestructura como Código?
Guía rápida: Define infraestructura cloud en TypeScript con seguridad de tipos total. Usa
ComponentResourcepara encapsular patrones reutilizables de infraestructura. Pasa{ parent: this }a todos los recursos hijos dentro de componentes. Usapulumi.interpolatepara construir cadenas con Outputs (no concatenación de strings). Nunca crees recursos dentro de.apply(). UsaConfig.requireSecret()para valores sensibles. Prefieretransformssobre las obsoletastransformations. Siempre llama athis.registerOutputs()al final de los constructores de componentes.
<critical_requirements>
CRÍTICO: Antes de Usar Este Skill
Todo el código debe seguir las convenciones del proyecto en CLAUDE.md (kebab-case, exportaciones nombradas, orden de importaciones,
import type, constantes nombradas)
(DEBES pasar { parent: this } a TODOS los recursos hijos dentro de un ComponentResource — omitirlo rompe el árbol de recursos y el seguimiento de estado)
(DEBES usar pulumi.interpolate para construir cadenas con Outputs — la concatenación de strings produce silenciosamente [object Object])
(NUNCA debes crear recursos dentro de .apply() — no aparecerán en pulumi preview y causarán problemas de orden)
(DEBES usar Config.requireSecret() para valores sensibles — Config.require() almacena valores como texto plano en el estado)
(DEBES llamar a this.registerOutputs() al final de cada constructor de ComponentResource — omitirlo impide el seguimiento de outputs)
</critical_requirements>
Recursos detallados:
- examples/core.md - Definiciones de recursos, recursos de componente, nomenclatura, Outputs, configuración/secretos
- examples/advanced.md - Referencias entre stacks, transformaciones, proveedores dinámicos, Automation API, policy packs
- reference.md - Marcos de decisión, tabla de opciones de recursos, referencia de API, comandos CLI
Auto-detección: 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
Cuándo usarlo:
- Definir infraestructura cloud en TypeScript con APIs de recursos con seguridad de tipos
- Crear componentes reutilizables de infraestructura con
ComponentResource - Gestionar arquitecturas multi-stack con referencias entre stacks
- Manejar secretos y configuración específica del entorno
- Construir plataformas de infraestructura autogestionadas con la Automation API
- Escribir políticas de cumplimiento con policy packs de CrossGuard
Cuándo NO usarlo:
- Scripts únicos que crean un solo recurso (usa directamente la CLI cloud)
- Proyectos donde el equipo no tiene experiencia en TypeScript (considera otras opciones de IaC)
Patrones clave cubiertos:
- Definiciones de recursos con entradas tipadas y nomenclatura automática
- Encapsulación con ComponentResource (parent, naming, registerOutputs)
- Outputs:
apply,all,interpolatey lifting - Configuración y gestión de secretos (
Config.require,Config.requireSecret,pulumi.secret) - Referencias entre stacks para compartir datos entre stacks
- Opciones de recursos (
dependsOn,protect,aliases,ignoreChanges,transforms) - Proveedores dinámicos para recursos CRUD personalizados
- Automation API para gestión programática de stacks
- Policy packs de CrossGuard para cumplimiento normativo
Filosofía
Pulumi trata la infraestructura como código real, no como archivos de configuración. TypeScript te brinda seguridad de tipos, autocompletado en IDE, herramientas de refactorización y todo el ecosistema de Node.js. Los recursos son objetos, las dependencias son automáticas y la reutilización ocurre a través de funciones y clases, no un lenguaje de módulos personalizado.
Principios fundamentales:
- Los recursos son objetos: Cada recurso cloud es una instancia de una clase TypeScript con entradas y salidas tipadas
- Las dependencias son automáticas: Cuando pasas la salida de un recurso como entrada de otro, Pulumi infiere el grafo de dependencias
- Reutilización mediante componentes:
ComponentResourceencapsula múltiples recursos en una unidad lógica única con sus propias entradas y salidas - Los Outputs son promesas:
Output<T>representa un valor que puede no conocerse hasta después del despliegue — usaapply,allointerpolatepara trabajar con ellos, nunca los desempaques manualmente - El estado se gestiona: Pulumi rastrea cada recurso en el estado — cambiar un nombre lógico o mover un recurso entre archivos provoca un borrado y recreación a menos que uses
aliases
Patrones Principales
Patrón 1: Definiciones de Recursos
Cada recurso toma un nombre lógico (usado para el seguimiento de estado), un conjunto de argumentos (entradas tipadas) y opciones de recurso opcionales.
const bucket = new aws.s3.Bucket(
"data-bucket",
{
versioning: { enabled: true },
lifecycleRules: [
{ enabled: true, expiration: { days: BUCKET_EXPIRY_DAYS } },
],
},
{ protect: true },
); // Prevenir borrado accidental
Puntos clave: Los nombres lógicos deben ser únicos por tipo dentro de un stack. Pulumi añade automáticamente un sufijo aleatorio al nombre físico para evitar colisiones. Usa protect: true en recursos críticos. Exporta outputs para consumo entre stacks.
Consulta examples/core.md para nomenclatura de recursos, configuración de nomenclatura automática y opciones de proveedor.
Patrón 2: Encapsulación con ComponentResource
Agrupa recursos relacionados en un ComponentResource para crear unidades reutilizables de infraestructura.
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,
});
}
}
Por qué es bueno: los recursos hijos usan { parent: this }, el nombre se prefija desde el padre, se llama a registerOutputs, el token de tipo sigue el formato pkg:module:Type
Consulta examples/core.md para patrones completos de componentes con herencia de proveedores y recursos multi-componente.
Patrón 3: Trabajar con Outputs
Los Outputs representan valores resueltos después del despliegue. Nunca uses concatenación de strings — usa interpolate, apply o all.
// interpolate — plantilla etiquetada para construir cadenas (preferido)
const url = pulumi.interpolate`https://${bucket.bucketRegionalDomainName}/index.html`;
// apply — transforma un solo output
const upper = bucket.id.apply((id) => id.toUpperCase());
// all — combina múltiples outputs
const endpoint = pulumi
.all([lb.dnsName, listener.port])
.apply(([dns, port]) => `http://${dns}:${port}`);
// Lifting — accede a propiedades directamente en los outputs del recurso
const subnetId = vpc.subnets[0].id; // No se necesita apply para propiedades conocidas
Por qué es bueno: interpolate maneja valores Output de forma transparente, all espera múltiples valores, el lifting evita llamadas apply innecesarias
Trampa: Los recursos creados dentro de .apply() no aparecerán en pulumi preview y pueden causar problemas de orden. Siempre pasa Outputs directamente como entradas a otros recursos.
Consulta examples/core.md para patrones de Output, envoltura con pulumi.output() y el anti-patrón de apply.
Patrón 4: Configuración y Secretos
Usa pulumi.Config para valores específicos del stack. Usa requireSecret para datos sensibles — cifra el valor en el estado.
const config = new pulumi.Config();
// Valores de configuración planos
const region = config.require("region"); // Falla si falta
const nodeCount = config.getNumber("nodeCount"); // Devuelve undefined si falta
// Valores secretos — cifrados en el estado
const dbPassword = config.requireSecret("dbPassword");
const apiKey = config.getSecret("apiKey");
// Marcar valores programáticos como secretos
const connectionString = pulumi.interpolate`postgres://admin:${dbPassword}@${db.endpoint}/mydb`;
// connectionString es automáticamente secreto porque dbPassword es secreto
// Marcar explícitamente un valor como secreto
const token = pulumi.secret(generateToken());
Punto clave: Cualquier Output derivado de un secreto se marca automáticamente como secreto. No necesitas volver a marcar los valores derivados.
Consulta examples/core.md para configuración con espacios de nombres, outputs secretos y comandos CLI de configuración.
Patrón 5: Opciones de Recursos
Las opciones de recursos controlan el comportamiento del ciclo de vida. Las más importantes:
const db = new aws.rds.Instance(
"primary-db",
{
/* ... */
},
{
protect: true, // Prevenir borrado accidental
dependsOn: [vpc, securityGroup], // Orden explícito
ignoreChanges: ["tags"], // Ignorar deriva en props específicos
aliases: [{ name: "old-db-name" }], // Renombrar sin recrear
retainOnDelete: true, // Mantener recurso cloud en pulumi destroy
deleteBeforeReplace: true, // Para recursos que deben ser únicos
replaceOnChanges: ["engine"], // Forzar reemplazo en cambios específicos
provider: usEastProvider, // Proveedor explícito (región, cuenta)
},
);
Trampa: Cambiar el nombre lógico o el padre de un recurso hace que Pulumi lo borre y lo recree. Usa aliases para renombrar de forma segura.
Consulta reference.md para la tabla completa de opciones de recursos.
Patrón 6: Referencias entre Stacks
Comparte outputs entre stacks usando StackReference.
// En el stack de red: exportar outputs
export const vpcId = vpc.id;
export const subnetIds = subnets.map((s) => s.id);
// En el stack de aplicación: consumir outputs
const networkStack = new pulumi.StackReference("myorg/networking/prod");
const vpcId = networkStack.getOutput("vpcId");
const subnetIds = networkStack.getOutput("subnetIds");
// requireOutput falla si el output no existe (más seguro que getOutput)
const vpcIdRequired = networkStack.requireOutput("vpcId");
Consulta examples/advanced.md para patrones de referencia entre stacks y getOutputDetails.
Patrón 7: Transformaciones (Transforms)
Aplica transformaciones a los recursos y sus hijos. Usa transforms (no las obsoletas transformations). Transforms reciben un objeto args con type, props y opts, y devuelven un resultado modificado o undefined para omitir.
// A nivel de recurso: aplicar tags a todos los hijos etiquetables de un componente
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;
},
],
},
);
Diferencia clave con las obsoletas transformations: las transforms soportan modificar hijos de componentes empaquetados (awsx, eks), soportan callbacks asíncronos y no pasan un objeto Resource.
Consulta examples/advanced.md para transforms a nivel de stack, modificación de opciones y migración desde transformations.
Patrón 8: Automation API
Ejecuta Pulumi programáticamente sin la CLI — para plataformas autogestionadas, pruebas de integración o herramientas de despliegue personalizadas.
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 });
Punto clave: La Automation API requiere que la CLI de Pulumi esté instalada y en el PATH — usa el motor de la CLI internamente.
Consulta examples/advanced.md para preview, destroy, modo de programa local, configuración y obtención de outputs del stack.
<red_flags>
BANDERAS ROJAS
Alta Prioridad:
- Crear recursos dentro de
.apply()— No aparecerán enpulumi preview, causan problemas de orden y rompen el grafo de dependencias. Pasa Outputs directamente como entradas de recursos. - Falta de
{ parent: this }en hijos de ComponentResource — Los recursos aparecen en la raíz del árbol de estado, rompiendo la agrupación lógica y el borrado en cascada del componente. - Concatenación de strings con Outputs —
"https://" + bucket.idproduce[object Object]. Usapulumi.interpolateen su lugar. - Usar
Config.require()para contraseñas/claves — Almacena el valor como texto plano en el estado. UsaConfig.requireSecret()para cifrar. - Olvidar
this.registerOutputs()— Los outputs del componente no se rastrearán correctamente en el estado ni estarán disponibles mediante referencias entre stacks. - Cambiar nombres lógicos sin aliases — Pulumi borra y recrea el recurso. Usa
aliases: [{ name: "old-name" }]para renombrar de forma segura.
Prioridad Media:
- Confiar en proveedores predeterminados en configuraciones multi-región — Usa proveedores explícitos por región. Establece
pulumi:disable-default-providersen la configuración para forzar. - Empezar con funciones en lugar de ComponentResource — Migrar más tarde requiere aliases o recreación de recursos. Usa componentes desde el principio.
- No usar
dependsOnpara dependencias no obvias — Pulumi infiere dependencias de la conexión Input/Output, pero efectos secundarios (propagación de IAM, etc.) pueden requerirdependsOn.
</red_flags>
Ejemplos de Comandos y Uso
Comandos CLI
# Inicializar un nuevo proyecto
pulumi new typescript --name my-infra --stack dev
# Configurar valores
pulumi config set aws:region us-east-1
pulumi config set dbPassword --secret
# Desplegar
pulumi up --stack dev
# Obtener outputs
pulumi stack output endpoint
# Destruir
pulumi destroy --stack dev
Declaración de importaciones
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
Exportar outputs
export const url = pulumi.interpolate`https://${bucket.bucketRegionalDomainName}`;
export const arn = bucket.arn;
Manejo de secretos
const config = new pulumi.Config();
export const dbPassword = config.requireSecret("dbPassword");
export const connectionString = pulumi.interpolate`postgres://admin:${dbPassword}@${db.endpoint}/mydb`;
Pattern típico de componente
class MyComponent extends pulumi.ComponentResource {
constructor(name: string, opts?: pulumi.ComponentResourceOptions) {
super("pkg:module:MyComponent", name, {}, opts);
// Crear recursos con { parent: this }
this.registerOutputs({});
}
}
Guía de Inicio Rápido
- Instalar Pulumi:
curl -fsSL https://get.pulumi.com | sh - Inicializar proyecto:
pulumi new typescripten tu directorio de infraestructura - Configurar proveedor cloud: Establece las credenciales y región adecuadas (ej.
pulumi config set aws:region us-east-1) - Definir recursos: Sigue los patrones de ComponentResource para estructurar tu infraestructura
- Gestionar secretos: Usa siempre
Config.requireSecret()para valores sensibles - Desplegar:
pulumi uppara revisar y desplegar los cambios
Consejos para empezar:
- Revisa los ejemplos en los archivos markdown vinculados para ver patrones completos
- Usa el autocompletado de TypeScript para explorar las APIs de los recursos
- Comienza con un stack pequeño y ve añadiendo complejidad gradualmente
- Configura un pipeline CI/CD con la Automation API para despliegues automatizados