pulumi-automation-api

작성자: pulumi

Pulumi 인프라 운영을 여러 스택과 애플리케이션에 걸쳐 프로그래밍 방식으로 조정합니다. 로컬 소스(기존 Pulumi 프로젝트)와 인라인 소스(내장 프로그램) 아키텍처를 모두 지원하여 단순한 시나리오부터 복잡한 다중 스택 시나리오까지 유연한 배포 패턴을 가능하게 합니다. 종속성 순서 지정, 병렬 독립 배포, 조정된 인프라 프로비저닝을 위한 스택 간 출력 전달을 통해 다중 스택 조정을 처리합니다. 프로그래밍 방식의...

npx skills add https://github.com/pulumi/agent-skills --skill pulumi-automation-api

Pulumi Automation API

When to Use This Skill

Invoke this skill when:

  • Orchestrating deployments across multiple Pulumi stacks
  • Embedding Pulumi operations in custom applications
  • Building self-service infrastructure platforms
  • Replacing fragile Bash/Makefile orchestration scripts
  • Creating custom CLIs for infrastructure management
  • Building web applications that provision infrastructure

What is Automation API

Automation API provides programmatic access to Pulumi operations. Instead of running pulumi up from the CLI, you call functions in your code that perform the same operations.

import * as automation from "@pulumi/pulumi/automation";

// Create or select a stack
const stack = await automation.LocalWorkspace.createOrSelectStack({
    stackName: "dev",
    projectName: "my-project",
    program: async () => {
        // Your Pulumi program here
    },
});

// Run pulumi up programmatically
const upResult = await stack.up({ onOutput: console.log });
console.log(`Update summary: ${JSON.stringify(upResult.summary)}`);

When to Use Automation API

Good Use Cases

Multi-stack orchestration:

When you split infrastructure into multiple focused projects, Automation API helps offset the added complexity by orchestrating operations across stacks:

infrastructure → platform → application
     ↓              ↓            ↓
   (VPC)      (Kubernetes)   (Services)

Automation API ensures correct sequencing without manual intervention.

Self-service platforms:

Build internal tools where developers request infrastructure without learning Pulumi:

  • Web portals for environment provisioning
  • Slack bots that create/destroy resources
  • Custom CLIs tailored to your organization

Embedded infrastructure:

Applications that provision their own infrastructure:

  • SaaS platforms creating per-tenant resources
  • Testing frameworks spinning up test environments
  • CI/CD systems with dynamic infrastructure needs

Replacing fragile scripts:

If you have Bash scripts or Makefiles stitching together multiple pulumi commands, Automation API provides:

  • Proper error handling
  • Type safety
  • Programmatic access to outputs

When NOT to Use

  • Single project with standard deployment needs
  • When you don't need programmatic control over operations

Architecture Choices

Local Source vs Inline Source

Local Source - Pulumi program in separate files:

const stack = await automation.LocalWorkspace.createOrSelectStack({
    stackName: "dev",
    workDir: "./infrastructure",  // Points to existing Pulumi project
});

When to use:

  • Different teams maintain orchestrator vs Pulumi programs
  • Pulumi programs already exist
  • Want independent version control and release cycles
  • Platform team orchestrating application team's infrastructure

Inline Source - Pulumi program embedded in orchestrator:

import * as aws from "@pulumi/aws";

const stack = await automation.LocalWorkspace.createOrSelectStack({
    stackName: "dev",
    projectName: "my-project",
    program: async () => {
        const bucket = new aws.s3.Bucket("my-bucket");
        return { bucketName: bucket.id };
    },
});

When to use:

  • Single team owns everything
  • Tight coupling between orchestration and infrastructure is desired
  • Distributing as compiled binary (no source files needed)
  • Simpler deployment artifact

Language Independence

The Automation API program can use a different language than the Pulumi programs it orchestrates:

Orchestrator (Go) → manages → Pulumi Program (TypeScript)

This enables platform teams to use their preferred language while application teams use theirs.

Common Patterns

Multi-Stack Orchestration

Deploy multiple stacks in dependency order:

import * as automation from "@pulumi/pulumi/automation";

async function deploy() {
    const stacks = [
        { name: "infrastructure", dir: "./infra" },
        { name: "platform", dir: "./platform" },
        { name: "application", dir: "./app" },
    ];

    for (const stackInfo of stacks) {
        console.log(`Deploying ${stackInfo.name}...`);

        const stack = await automation.LocalWorkspace.createOrSelectStack({
            stackName: "prod",
            workDir: stackInfo.dir,
        });

        await stack.up({ onOutput: console.log });
        console.log(`${stackInfo.name} deployed successfully`);
    }
}

async function destroy() {
    // Destroy in reverse order
    const stacks = [
        { name: "application", dir: "./app" },
        { name: "platform", dir: "./platform" },
        { name: "infrastructure", dir: "./infra" },
    ];

    for (const stackInfo of stacks) {
        console.log(`Destroying ${stackInfo.name}...`);

        const stack = await automation.LocalWorkspace.selectStack({
            stackName: "prod",
            workDir: stackInfo.dir,
        });

        await stack.destroy({ onOutput: console.log });
    }
}

Passing Configuration

Set stack configuration programmatically:

const stack = await automation.LocalWorkspace.createOrSelectStack({
    stackName: "dev",
    workDir: "./infrastructure",
});

// Set configuration values
await stack.setConfig("aws:region", { value: "us-west-2" });
await stack.setConfig("dbPassword", { value: "secret", secret: true });

// Then deploy
await stack.up();

Reading Outputs

Access stack outputs after deployment:

const upResult = await stack.up();

// Get all outputs
const outputs = await stack.outputs();
console.log(`VPC ID: ${outputs["vpcId"].value}`);

// Or from the up result
console.log(`Outputs: ${JSON.stringify(upResult.outputs)}`);

Error Handling

Handle deployment failures gracefully:

try {
    const result = await stack.up({ onOutput: console.log });

    if (result.summary.result === "failed") {
        console.error("Deployment failed");
        process.exit(1);
    }
} catch (error) {
    console.error(`Deployment error: ${error}`);
    throw error;
}

Parallel Stack Operations

When stacks are independent, deploy in parallel:

const independentStacks = [
    { name: "service-a", dir: "./service-a" },
    { name: "service-b", dir: "./service-b" },
    { name: "service-c", dir: "./service-c" },
];

await Promise.all(independentStacks.map(async (stackInfo) => {
    const stack = await automation.LocalWorkspace.createOrSelectStack({
        stackName: "prod",
        workDir: stackInfo.dir,
    });
    return stack.up({ onOutput: (msg) => console.log(`[${stackInfo.name}] ${msg}`) });
}));

Best Practices

Separate Configuration from Code

Externalize configuration into files or environment variables:

import * as fs from "fs";

interface DeployConfig {
    stacks: Array<{ name: string; dir: string; }>;
    environment: string;
}

const config: DeployConfig = JSON.parse(
    fs.readFileSync("./deploy-config.json", "utf-8")
);

for (const stackInfo of config.stacks) {
    const stack = await automation.LocalWorkspace.createOrSelectStack({
        stackName: config.environment,
        workDir: stackInfo.dir,
    });
    await stack.up();
}

This enables distributing compiled binaries without exposing source code.

Stream Output for Long Operations

Use onOutput callback for real-time feedback:

await stack.up({
    onOutput: (message) => {
        process.stdout.write(message);
        // Or send to logging system, websocket, etc.
    },
});

Quick Reference

ScenarioApproach
Existing Pulumi projectsLocal source with workDir
New embedded infrastructureInline source with program function
Different teamsLocal source for independence
Compiled binary distributionInline source or bundled local
Multi-stack dependenciesSequential deployment in order
Independent stacksParallel deployment with Promise.all

Related Skills

  • pulumi-best-practices: Code-level patterns for Pulumi programs

References

pulumi의 다른 스킬

pulumi-debug-failed-operation
pulumi
실패한 Pulumi 업데이트 또는 프리뷰를 디버그합니다: Pulumi가 이미 기록한 실패를 읽고, 원인을 찾아 수정합니다. 사용자가 요청하면 이 스킬을 로드합니다…
provider-upgrade
pulumi
공급자 업그레이드는 변경 요청이 아닌 번역입니다.
cloudformation-to-pulumi
pulumi
AWS CloudFormation 스택이나 템플릿을 Pulumi 프로그램으로 변환, 마이그레이션 또는 가져옵니다. 사용자가 CloudFormation에서 이동하려고 할 때 이 스킬을 로드하세요.
pulumi-arm-to-pulumi
pulumi
ARM 템플릿, Bicep 또는 기존 Azure 리소스를 Pulumi 인프라 코드로 변환합니다. 매개변수, 변수, 루프, 조건문 및 중첩 템플릿을 지원하는 완전한 ARM 템플릿 변환을 Pulumi(TypeScript, Python, Go, C#, Java 또는 YAML)로 처리합니다. azure-native(전체 API 지원) 및 azure(클래식, 단순화) 공급자를 모두 지원하며, 각 리소스에 적합한 공급자를 자동으로 선택합니다. 기존에 배포된 Azure 리소스를 제로-차이 검증과 함께 Pulumi로 가져옵니다...
pulumi-cdk-to-pulumi
pulumi
사용자가 AWS CDK 애플리케이션(CDK 스택, 구조, 또는 포함)을 마이그레이션, 변환, 포팅, 번역 또는 이동하려고 할 때 이 스킬을 로드하세요.
pulumi-migrate-from-discovered-stack
pulumi
CloudFormation 또는 ARM 스택을 Pulumi 스택으로 마이그레이션하며, Pulumi Cloud의 Discovery 기능이 이미 발견하여 …를 통해 노출한 스택을 소스로 사용합니다.
package-usage
pulumi
Pulumi 조직 내 여러 스택에서 특정 패키지를 어떤 버전으로 사용하는지 추적합니다. 스택 간 감사, 오래되었거나 유지 관리되지 않는 패키지 식별에 사용됩니다.
pulumi-best-practices
pulumi
신뢰할 수 있고 유지보수 가능한 Pulumi 인프라 코드를 작성하기 위한 종합적인 모범 사례. apply() 콜백 내에서 리소스를 생성하지 말고, Output 객체를 입력으로 직접 전달하여 종속성 추적 및 미리보기 가시성을 유지하세요. ComponentResource 클래스를 사용하여 관련 리소스를 재사용 가능한 논리적 단위로 그룹화하고, parent: this를 통해 적절한 부모-자식 계층 구조를 설정하세요. --secret 플래그 또는 config.requireSecret()를 사용하여 처음부터 비밀을 암호화하고, 상태 파일에서 자격 증명 유출을 방지하세요...