SKILL.md
$2a
// 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.
},
});