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() 加密機密資訊,以防止憑證在狀態檔案中洩漏...