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.

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 defines the infrastructure
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 attempts deploy and cleans up on failure
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 deploy with exponential backoff for transient failures
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)
	}
	fmt.Printf("Preview: %d changes\n", len(preview.Steps))

	// 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