Communitygithub.com

Pulumi Infraestrutura como Código

Defina infraestrutura em nuvem com TypeScript, com segurança total de tipos, usando Pulumi.

O que é Pulumi Infraestrutura como Código?

Este skill capacita agentes de IA (como Claude Code, Cursor, Codex) a definir e gerenciar infraestrutura em nuvem como código real usando Pulumi com TypeScript. Suporta todos os principais provedores de nuvem (AWS, GCP, Azure, Kubernetes) através dos pacotes @pulumi. O skill enfatiza práticas essenciais: encapsular recursos reutilizáveis com `ComponentResource` (sempre passando `{ parent: this }`), construir strings que envolvem `Output` com `pulumi.interpolate` (evitando concatenação), gerenciar segredos com `Config.requireSecret()`, e chamar `this.registerOutputs()` no final de construtores de componentes. Ele também cobre patterns avançados como Stack References para compartilhar dados entre stacks, Transforms (substituto moderno para `transformations`), Dynamic Providers para lógica CRUD personalizada, e a Automation API para gerenciar stacks programaticamente. O skill é auto-detectado pela presença de pacotes Pulumi no código e é ideal para projetos que exigem componentes de infraestrutura reutilizáveis e type-safe, mas não para scripts únicos ou times sem experiência em TypeScript.

Funciona com✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/agents-inc/skills/tree/HEAD/src/skills/infra-iac-pulumi

Perguntar na sua IA favorita

Abre um novo chat com esta habilidade de agente já pré-carregada.

Documentação

O que Pulumi Infraestrutura como Código faz?

Guia Rápido: Defina infraestrutura em nuvem em TypeScript com segurança total de tipos. Use ComponentResource para encapsular padrões de infraestrutura reutilizáveis. Passe { parent: this } para todos os recursos filhos dentro de componentes. Use pulumi.interpolate para construir strings com Outputs (não concatenação de strings). Nunca crie recursos dentro de .apply(). Use Config.requireSecret() para valores sensíveis. Prefira transforms em vez de transformations (obsoleto). Sempre chame this.registerOutputs() ao final dos construtores de componentes.


<critical_requirements>

CRÍTICO: Antes de Usar Este Skill

Todo código deve seguir as convenções do projeto em CLAUDE.md (kebab-case, exports nomeados, ordem de imports, import type, constantes nomeadas)

(Você DEVE passar { parent: this } para TODOS os recursos filhos dentro de um ComponentResource — omitir isso quebra a árvore de recursos e o rastreamento de estado)

(Você DEVE usar pulumi.interpolate para construir strings com Outputs — a concatenação de strings produz silenciosamente [object Object])

(Você NUNCA DEVE criar recursos dentro de .apply() — eles não aparecerão em pulumi preview e causarão problemas de ordenação)

(Você DEVE usar Config.requireSecret() para valores sensíveis — Config.require() armazena valores como texto simples no estado)

(Você DEVE chamar this.registerOutputs() ao final de todo construtor de ComponentResource — omitir isso impede o rastreamento de saídas)

</critical_requirements>


Recursos Detalhados:

  • examples/core.md - Definições de recursos, componentes, nomenclatura, Outputs, configuração/segredos
  • examples/advanced.md - Referências de stack, transforms, provedores dinâmicos, Automation API, pacotes de política
  • reference.md - Estruturas de decisão, tabela de opções de recursos, referência de API, comandos CLI

Auto-detecção: 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

Quando usar:

  • Definir infraestrutura em nuvem em TypeScript com APIs de recursos type-safe
  • Criar componentes de infraestrutura reutilizáveis com ComponentResource
  • Gerenciar arquiteturas multi-stack com referências de stack
  • Lidar com segredos e configuração específica de ambiente
  • Construir plataformas de infraestrutura self-service com a Automation API
  • Escrever políticas de conformidade com pacotes de política CrossGuard

Quando NÃO usar:

  • Scripts avulsos que criam um único recurso (use a CLI da nuvem diretamente)
  • Projetos onde a equipe não tem experiência em TypeScript (considere outras opções de linguagem IaC)

Padrões principais abordados:

  • Definições de recursos com entradas tipadas e nomenclatura automática
  • Encapsulamento de ComponentResource (parent, nomenclatura, registerOutputs)
  • Outputs: apply, all, interpolate e lifting
  • Gerenciamento de configuração e segredos (Config.require, Config.requireSecret, pulumi.secret)
  • Referências de stack para compartilhamento de dados entre stacks
  • Opções de recursos (dependsOn, protect, aliases, ignoreChanges, transforms)
  • Provedores dinâmicos para recursos CRUD personalizados
  • Automation API para gerenciamento programático de stacks
  • Pacotes de política CrossGuard para aplicação de conformidade

Filosofia

Pulumi trata infraestrutura como código real, não arquivos de configuração. TypeScript oferece segurança de tipos, autocompletar na IDE, ferramentas de refatoração e todo o ecossistema Node.js. Recursos são objetos, dependências são automáticas e a reutilização acontece através de funções e classes — não uma linguagem de módulo personalizada.

Princípios centrais:

  • Recursos são objetos: Cada recurso de nuvem é uma instância de classe TypeScript com entradas e saídas tipadas
  • Dependências são automáticas: Quando você passa a saída de um recurso como entrada de outro, o Pulumi infere o grafo de dependências
  • Reutilização através de componentes: ComponentResource encapsula múltiplos recursos em uma única unidade lógica com suas próprias entradas e saídas
  • Outputs são promessas: Output<T> representa um valor que pode não ser conhecido até após a implantação — use apply, all ou interpolate para trabalhar com eles, nunca desempacote manualmente
  • Estado é gerenciado: Pulumi rastreia cada recurso no estado — mudar um nome lógico ou mover um recurso entre arquivos dispara um delete-e-recria, a menos que você use aliases

Padrões Principais

Padrão 1: Definições de Recursos

Cada recurso recebe um nome lógico (usado para rastreamento de estado), um saco de argumentos (entradas tipadas) e opções de recurso opcionais.

const bucket = new aws.s3.Bucket(
  "data-bucket",
  {
    versioning: { enabled: true },
    lifecycleRules: [
      { enabled: true, expiration: { days: BUCKET_EXPIRY_DAYS } },
    ],
  },
  { protect: true },
); // Impede exclusão acidental

Pontos-chave: Nomes lógicos devem ser únicos por tipo dentro de um stack. Pulumi anexa automaticamente um sufixo aleatório ao nome físico para evitar colisões. Use protect: true em recursos críticos. Exporte saídas para consumo entre stacks.

Veja examples/core.md para nomenclatura de recursos, configuração de nomenclatura automática e opções de provedor.


Padrão 2: Encapsulamento de ComponentResource

Agrupe recursos relacionados em um ComponentResource para criar unidades de infraestrutura reutilizáveis.

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 que é bom: recursos filhos usam { parent: this }, nome é prefixado do pai, registerOutputs é chamado, token de tipo segue o formato pkg:module:Type

Veja examples/core.md para padrões completos de componentes com herança de provedor e componentes multi-recursos.


Padrão 3: Trabalhando com Outputs

Outputs representam valores resolvidos após a implantação. Nunca use concatenação de strings — use interpolate, apply ou all.

// interpolate -- template tag para construção de strings (preferido)
const url = pulumi.interpolate`https://${bucket.bucketRegionalDomainName}/index.html`;

// apply -- transforma um único output
const upper = bucket.id.apply((id) => id.toUpperCase());

// all -- combina múltiplos outputs
const endpoint = pulumi
  .all([lb.dnsName, listener.port])
  .apply(([dns, port]) => `http://${dns}:${port}`);

// Lifting -- acessa propriedades diretamente em saídas de recursos
const subnetId = vpc.subnets[0].id; // Não precisa de apply para propriedades conhecidas

Por que é bom: interpolate lida com valores Output de forma transparente, all espera por múltiplos valores, lifting evita chamadas apply desnecessárias

Pegadinha: Recursos criados dentro de .apply() não aparecerão em pulumi preview e podem causar problemas de ordenação. Sempre passe Outputs diretamente como entradas para outros recursos.

Veja examples/core.md para padrões de Output, encapsulamento pulumi.output() e o antipadrão apply.


Padrão 4: Config e Segredos

Use pulumi.Config para valores específicos de stack. Use requireSecret para dados sensíveis — ele criptografa o valor no estado.

const config = new pulumi.Config();

// Valores de configuração comuns
const region = config.require("region"); // Falha se ausente
const nodeCount = config.getNumber("nodeCount"); // Retorna undefined se ausente

// Valores secretos -- criptografados no 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 é automaticamente secreta porque dbPassword é secreto

// Marcar explicitamente um valor como secreto
const token = pulumi.secret(generateToken());

Ponto-chave: Qualquer Output derivado de um segredo é automaticamente marcado como secreto. Você não precisa re-marcar valores derivados.

Veja examples/core.md para config com namespace, saídas secretas e comandos CLI de configuração.


Padrão 5: Opções de Recursos

As opções de recurso controlam o comportamento do ciclo de vida. As mais importantes:

const db = new aws.rds.Instance(
  "primary-db",
  {
    /* ... */
  },
  {
    protect: true, // Impede exclusão acidental
    dependsOn: [vpc, securityGroup], // Ordenação explícita
    ignoreChanges: ["tags"], // Ignora drift em props específicas
    aliases: [{ name: "old-db-name" }], // Renomeia sem recriar
    retainOnDelete: true, // Mantém recurso na nuvem ao executar pulumi destroy
    deleteBeforeReplace: true, // Para recursos que devem ser únicos
    replaceOnChanges: ["engine"], // Força substituição em mudanças específicas
    provider: usEastProvider, // Provedor explícito (região, conta)
  },
);

Pegadinha: Mudar o nome lógico ou pai de um recurso faz o Pulumi excluí-lo e recriá-lo. Use aliases para renomear com segurança.

Veja reference.md para a tabela completa de opções de recursos.


Padrão 6: Referências de Stack

Compartilhe saídas entre stacks usando StackReference.

// No stack de rede: exportar saídas
export const vpcId = vpc.id;
export const subnetIds = subnets.map((s) => s.id);

// No stack de aplicação: consumir saídas
const networkStack = new pulumi.StackReference("myorg/networking/prod");
const vpcId = networkStack.getOutput("vpcId");
const subnetIds = networkStack.getOutput("subnetIds");

// requireOutput falha se a saída não existir (mais seguro que getOutput)
const vpcIdRequired = networkStack.requireOutput("vpcId");

Veja examples/advanced.md para padrões de referência de stack e getOutputDetails.


Padrão 7: Transforms

Aplique transformações a recursos e seus filhos. Use transforms (não o obsoleto transformations). Transforms recebem um objeto args com type, props e opts, e retornam um resultado modificado ou undefined para pular.

// Nível de recurso: aplicar tags a todos os filhos taggeáveis de um 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;
      },
    ],
  },
);

Diferença chave do obsoleto transformations: transforms suportam modificar filhos de componentes empacotados (awsx, eks), suportam callbacks assíncronos e não passam um objeto Resource.

Veja examples/advanced.md para transforms em nível de stack, modificação de opções e migração de transformations.


Padrão 8: Automation API

Execute Pulumi programaticamente sem a CLI — para plataformas self-service, testes de integração ou ferramentas de implantação 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 });

Ponto-chave: A Automation API requer que a CLI do Pulumi esteja instalada e no PATH — ela usa o motor da CLI internamente.

Veja examples/advanced.md para preview, destroy, modo de programa local, configuração e recuperação de saídas de stack.


<red_flags>

BANDEIRAS VERMELHAS

Alta Prioridade:

  • Criar recursos dentro de .apply() — Eles não aparecerão em pulumi preview, causam problemas de ordenação e quebram o grafo de dependências. Passe Outputs diretamente como entradas de recursos.
  • Faltando { parent: this } em filhos de ComponentResource — Recursos aparecem na raiz da árvore de estado, quebrando o agrupamento lógico e a cascata de exclusão do componente.
  • Concatenação de strings com Outputs — "https://" + bucket.id produz [object Object]. Use pulumi.interpolate em vez disso.
  • Usar Config.require() para senhas/chaves — Armazena o valor como texto simples no estado. Use Config.requireSecret() para criptografar.
  • Esquecer this.registerOutputs() — As saídas do componente não serão rastreadas corretamente no estado nem disponíveis via referências de stack.
  • Mudar nomes lógicos sem aliases — Pulumi exclui e recria o recurso. Use aliases: [{ name: "old-name" }] para renomear com segurança.

Média Prioridade:

  • Depender de provedores padrão em configurações multi-região — Use provedores explícitos por região. Defina pulumi:disable-default-providers na configuração para impor.
  • Começar com funções em vez de ComponentResource — Migrar depois requer aliases ou recriação de recursos. Use componentes desde o início.
  • Não usar dependsOn para dependências não óbvias — Pulumi infere dependências da fiação Input/Output, mas efeitos colaterais (propagação IAM

Individual skills in this repo

This repo contains 11 individual skills — each has its own dedicated page.

Habilidades Relacionadas