Pulumi Stack Deployer

A Go program that uses the Pulumi Automation API to programmatically create infrastructure stacks, configure them, preview changes, and deploy, all without shelling out to the Pulumi CLI.

Setup

Install the Pulumi CLI first — the Automation API requires it at runtime:

# macOS
brew install pulumi

# Linux
curl -fsSL https://get.pulumi.com | sh

Then install the AWS plugin and set up the project:

pulumi plugin install resource aws

mkdir pulumi-stack-deployer
cd pulumi-stack-deployer
go mod init github.com/yourorg/pulumi-stack-deployer

go get github.com/pulumi/pulumi/sdk/v3@latest
go get github.com/pulumi/pulumi/sdk/v3/go/auto@latest
go get github.com/pulumi/pulumi/sdk/v3/go/auto/optup@latest
go get github.com/pulumi/pulumi/sdk/v3/go/pulumi@latest
go get github.com/pulumi/pulumi-aws/sdk/v6@latest
go get github.com/pulumi/pulumi-aws/sdk/v6/go/aws/s3@latest

Set your AWS credentials:

export AWS_ACCESS_KEY_ID=your-key
export AWS_SECRET_ACCESS_KEY=your-secret
export AWS_REGION=us-west-2
package main

import (
	"context"
	"fmt"
	"os"
	"time"

	"github.com/pulumi/pulumi-aws/sdk/v6/go/aws/s3"
	"github.com/pulumi/pulumi/sdk/v3/go/auto"
	"github.com/pulumi/pulumi/sdk/v3/go/auto/optup"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

// deployFunc declares the infrastructure for this stack: an S3 bucket with
// website hosting. Passed as an inline program to UpsertStackInlineSource.
func deployFunc(ctx *pulumi.Context) error {
	bucket, err := s3.NewBucket(ctx, "my-bucket", &s3.BucketArgs{
		Website: &s3.BucketWebsiteArgs{
			IndexDocument: pulumi.String("index.html"),
		},
	})
	if err != nil {
		return err
	}
	ctx.Export("bucketName", bucket.Bucket)
	return nil
}

// deployWithRecovery runs stack.Up and destroys partially-created resources if it fails.
func deployWithRecovery(ctx context.Context, stack auto.Stack) error {
	result, err := stack.Up(ctx, optup.ProgressStreams(os.Stdout))
	if err != nil {
		fmt.Printf("Deploy failed: %v\n", err)

		// Destroy partially-created resources
		_, destroyErr := stack.Destroy(ctx)
		if destroyErr != nil {
			return fmt.Errorf("deploy failed and cleanup failed: %w (original: %v)", destroyErr, err)
		}
		return fmt.Errorf("deploy failed, resources cleaned up: %w", err)
	}

	fmt.Printf("Deploy succeeded: %s\n", result.Summary.Message)
	return nil
}

// deployWithRetry retries stack.Up up to maxRetries times with exponential backoff.
// It refreshes state before each retry to reconcile with actual cloud resources.
func deployWithRetry(ctx context.Context, stack auto.Stack, maxRetries int) error {
	for attempt := 0; attempt <= maxRetries; attempt++ {
		_, err := stack.Up(ctx)
		if err == nil {
			return nil
		}

		if attempt == maxRetries {
			return fmt.Errorf("deploy failed after %d attempts: %w", maxRetries, err)
		}

		// Refresh state to reconcile with reality before retrying
		_, _ = stack.Refresh(ctx)

		backoff := time.Duration(1<<uint(attempt)) * time.Second
		time.Sleep(backoff)
	}
	return nil
}

func main() {
	ctx := context.Background()

	// Create or select a stack
	stackName := "dev"
	projectName := "infra-service"

	stack, err := auto.UpsertStackInlineSource(ctx, stackName, projectName, deployFunc)
	if err != nil {
		panic(err)
	}

	// Set AWS region config
	stack.SetConfig(ctx, "aws:region", auto.ConfigValue{Value: "us-west-2"})

	// Set secrets (encrypted in state)
	stack.SetConfig(ctx, "dbPassword", auto.ConfigValue{
		Value:  "supersecret",
		Secret: true,
	})

	// Set multiple config values
	stack.SetAllConfig(ctx, auto.ConfigMap{
		"aws:region":   {Value: "us-west-2"},
		"app:replicas": {Value: "3"},
		"app:apiKey":   {Value: "key-123", Secret: true},
	})

	// Preview changes
	preview, err := stack.Preview(ctx)
	if err != nil {
		panic(err)
	}
	totalChanges := 0
	for _, count := range preview.ChangeSummary {
		totalChanges += count
	}
	fmt.Printf("Preview: %d changes\n", totalChanges)

	// Deploy with recovery (cleans up on failure)
	if err := deployWithRecovery(ctx, stack); err != nil {
		fmt.Printf("Recovery deploy failed: %v\n", err)
		// Fallback: retry with backoff
		if retryErr := deployWithRetry(ctx, stack, 3); retryErr != nil {
			fmt.Printf("All retries exhausted: %v\n", retryErr)
			os.Exit(1)
		}
	}

	// Print outputs
	outputs, err := stack.Outputs(ctx)
	if err == nil {
		fmt.Printf("Bucket: %s\n", outputs["bucketName"].Value)
	}
}

💻 Run locally

Copy the code above and run it on your machine

© 2026 ByteLearn.dev. Free courses for developers. · Privacy