Use when adding new error messages to React, or seeing "unknown error code" warnings.
npx skills add dirien/claude-skills --skill "pulumi-go"
Install specific skill from multi-skill repository
# Description
This skill should be used when the user asks to "create Pulumi Go project", "write Pulumi Go code", "use Pulumi ESC with Go", "set up OIDC for Pulumi", or mentions Pulumi infrastructure automation with Golang.
# SKILL.md
name: pulumi-go
description: This skill should be used when the user asks to "create Pulumi Go project", "write Pulumi Go code", "use Pulumi ESC with Go", "set up OIDC for Pulumi", or mentions Pulumi infrastructure automation with Golang.
version: 1.3.0
Pulumi Go Skill
Development Workflow
1. Project Setup
# Create new Go project
pulumi new go
# Or with a cloud-specific template
pulumi new aws-go
pulumi new azure-go
pulumi new gcp-go
Project structure:
my-project/
├── Pulumi.yaml
├── Pulumi.dev.yaml # Stack config (use ESC instead)
├── go.mod
├── go.sum
└── main.go
2. Pulumi ESC Integration
Instead of using pulumi config set or stack config files, use Pulumi ESC for centralized secrets and configuration.
Link ESC environment to stack:
# Create ESC environment
pulumi env init myorg/myproject-dev
# Edit environment
pulumi env edit myorg/myproject-dev
# Link to Pulumi stack
pulumi config env add myorg/myproject-dev
ESC environment definition (YAML):
values:
# Static configuration
pulumiConfig:
aws:region: us-west-2
myapp:instanceType: t3.medium
# Dynamic OIDC credentials for AWS
aws:
login:
fn::open::aws-login:
oidc:
roleArn: arn:aws:iam::123456789:role/pulumi-oidc
sessionName: pulumi-deploy
# Pull secrets from AWS Secrets Manager
secrets:
fn::open::aws-secrets:
region: us-west-2
login: ${aws.login}
get:
dbPassword:
secretId: prod/database/password
# Expose to environment variables
environmentVariables:
AWS_ACCESS_KEY_ID: ${aws.login.accessKeyId}
AWS_SECRET_ACCESS_KEY: ${aws.login.secretAccessKey}
AWS_SESSION_TOKEN: ${aws.login.sessionToken}
3. Go Patterns
Basic resource creation:
package main
import (
"github.com/pulumi/pulumi-aws/sdk/v6/go/aws/s3"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi/config"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
// Get configuration from ESC
cfg := config.New(ctx, "")
instanceType := cfg.Require("instanceType")
// Create resources with proper tagging
bucket, err := s3.NewBucket(ctx, "my-bucket", &s3.BucketArgs{
Versioning: &s3.BucketVersioningArgs{
Enabled: pulumi.Bool(true),
},
ServerSideEncryptionConfiguration: &s3.BucketServerSideEncryptionConfigurationArgs{
Rule: &s3.BucketServerSideEncryptionConfigurationRuleArgs{
ApplyServerSideEncryptionByDefault: &s3.BucketServerSideEncryptionConfigurationRuleApplyServerSideEncryptionByDefaultArgs{
SseAlgorithm: pulumi.String("AES256"),
},
},
},
Tags: pulumi.StringMap{
"Environment": pulumi.String(ctx.Stack()),
"ManagedBy": pulumi.String("Pulumi"),
},
})
if err != nil {
return err
}
// Export outputs
ctx.Export("bucketName", bucket.ID())
ctx.Export("bucketArn", bucket.Arn)
return nil
})
}
Component resources for reusability:
package main
import (
"github.com/pulumi/pulumi-aws/sdk/v6/go/aws/lb"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
type WebServiceArgs struct {
Port pulumi.IntInput
ImageUri pulumi.StringInput
}
type WebService struct {
pulumi.ResourceState
URL pulumi.StringOutput `pulumi:"url"`
}
func NewWebService(ctx *pulumi.Context, name string, args *WebServiceArgs, opts ...pulumi.ResourceOption) (*WebService, error) {
component := &WebService{}
err := ctx.RegisterComponentResource("custom:app:WebService", name, component, opts...)
if err != nil {
return nil, err
}
// Create child resources with pulumi.Parent(component)
loadBalancer, err := lb.NewLoadBalancer(ctx, name+"-lb", &lb.LoadBalancerArgs{
LoadBalancerType: pulumi.String("application"),
// ... configuration
}, pulumi.Parent(component))
if err != nil {
return nil, err
}
component.URL = loadBalancer.DnsName
ctx.RegisterResourceOutputs(component, pulumi.Map{
"url": component.URL,
})
return component, nil
}
Stack references for cross-stack dependencies:
package main
import (
"fmt"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
// Reference outputs from networking stack
networkingStack, err := pulumi.NewStackReference(ctx, "myorg/networking/prod", nil)
if err != nil {
return err
}
vpcId := networkingStack.GetStringOutput(pulumi.String("vpcId"))
subnetIds := networkingStack.GetOutput(pulumi.String("privateSubnetIds"))
// Use in resource creation
ctx.Export("vpcId", vpcId)
return nil
})
}
Working with Outputs:
package main
import (
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
// Apply transformation
uppercaseName := bucket.ID().ApplyT(func(id string) string {
return strings.ToUpper(id)
}).(pulumi.StringOutput)
// Combine multiple outputs
combined := pulumi.All(bucket.ID(), bucket.Arn).ApplyT(
func(args []interface{}) string {
id := args[0].(string)
arn := args[1].(string)
return fmt.Sprintf("Bucket %s has ARN %s", id, arn)
},
).(pulumi.StringOutput)
// Conditional resources
if ctx.Stack() == "prod" {
_, err := cloudwatch.NewMetricAlarm(ctx, "alarm", &cloudwatch.MetricAlarmArgs{
// ... configuration
})
if err != nil {
return err
}
}
return nil
})
}
4. Using ESC with pulumi env run
Run any command with ESC environment variables injected:
# Run pulumi commands with ESC credentials
pulumi env run myorg/aws-dev -- pulumi up
# Run tests with secrets
pulumi env run myorg/test-env -- go test ./...
# Open environment and export to shell
pulumi env open myorg/myproject-dev --format shell
5. Error Handling Patterns
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
// Always check errors
bucket, err := s3.NewBucket(ctx, "bucket", &s3.BucketArgs{})
if err != nil {
return fmt.Errorf("failed to create bucket: %w", err)
}
// Chain operations with error handling
policy, err := s3.NewBucketPolicy(ctx, "policy", &s3.BucketPolicyArgs{
Bucket: bucket.ID(),
Policy: bucket.Arn.ApplyT(func(arn string) string {
return fmt.Sprintf(`{"Version":"2012-10-17",...}`, arn)
}).(pulumi.StringOutput),
})
if err != nil {
return fmt.Errorf("failed to create bucket policy: %w", err)
}
return nil
})
}
6. Multi-Language Components
Create components in Go that can be consumed from any Pulumi language (TypeScript, Python, C#, Java, YAML).
Project structure for multi-language component:
my-component/
├── PulumiPlugin.yaml # Required for multi-language
├── go.mod
├── go.sum
└── main.go # Component + entry point
PulumiPlugin.yaml:
runtime: go
Component with proper Args struct (main.go):
package main
import (
"context"
"log"
"github.com/pulumi/pulumi-aws/sdk/v6/go/aws/s3"
"github.com/pulumi/pulumi-go-provider/infer"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
// Args struct - use Input types for all properties
type SecureBucketArgs struct {
BucketName pulumi.StringInput `pulumi:"bucketName"`
EnableVersioning pulumi.BoolInput `pulumi:"enableVersioning,optional"`
Tags pulumi.StringMapInput `pulumi:"tags,optional"`
}
type SecureBucket struct {
pulumi.ResourceState
BucketId pulumi.StringOutput `pulumi:"bucketId"`
BucketArn pulumi.StringOutput `pulumi:"bucketArn"`
}
func NewSecureBucket(ctx *pulumi.Context, name string, args *SecureBucketArgs, opts ...pulumi.ResourceOption) (*SecureBucket, error) {
component := &SecureBucket{}
err := ctx.RegisterComponentResource("myorg:storage:SecureBucket", name, component, opts...)
if err != nil {
return nil, err
}
bucket, err := s3.NewBucket(ctx, name+"-bucket", &s3.BucketArgs{
Bucket: args.BucketName,
Versioning: &s3.BucketVersioningArgs{
Enabled: args.EnableVersioning,
},
ServerSideEncryptionConfiguration: &s3.BucketServerSideEncryptionConfigurationArgs{
Rule: &s3.BucketServerSideEncryptionConfigurationRuleArgs{
ApplyServerSideEncryptionByDefault: &s3.BucketServerSideEncryptionConfigurationRuleApplyServerSideEncryptionByDefaultArgs{
SseAlgorithm: pulumi.String("AES256"),
},
},
},
Tags: args.Tags,
}, pulumi.Parent(component))
if err != nil {
return nil, err
}
component.BucketId = bucket.ID().ToStringOutput()
component.BucketArn = bucket.Arn
ctx.RegisterResourceOutputs(component, pulumi.Map{
"bucketId": component.BucketId,
"bucketArn": component.BucketArn,
})
return component, nil
}
// Entry point for multi-language support
func main() {
prov, err := infer.NewProviderBuilder().
WithNamespace("myorg").
WithComponents(
infer.ComponentF(NewSecureBucket),
).
Build()
if err != nil {
log.Fatal(err.Error())
}
_ = prov.Run(context.Background(), "go-components", "v0.0.1")
}
Publishing for multi-language consumption:
# Consume from git repository
pulumi package add github.com/myorg/my-component
# With version tag
pulumi package add github.com/myorg/[email protected]
# Local development
pulumi package add /path/to/local/my-component
Multi-language Args requirements:
- Use pulumi.*Input types for all properties
- Use pulumi:"fieldName" struct tags
- Add ,optional tag suffix for optional fields
- Avoid interface{} or unsupported types
Best Practices
Security
- Use Pulumi ESC for all secrets - never commit secrets to stack config files
- Enable OIDC authentication instead of static credentials
- Use dynamic secrets with short TTLs when possible
- Apply least-privilege IAM policies
Code Organization
- Use Component Resources for reusable infrastructure patterns
- Leverage Go's type system for configuration validation
- Keep stack-specific config in ESC environments
- Use stack references for cross-stack dependencies
- Handle all errors explicitly
Deployment
- Always run
pulumi previewbeforepulumi up - Use ESC environment versioning and tags for releases
- Implement proper tagging strategy for all resources
- Build your Go program before running Pulumi:
go build -o $(basename $(pwd))
Common Commands
# Environment Commands (pulumi env)
pulumi env init <org>/<project>/<env> # Create environment
pulumi env edit <org>/<env> # Edit environment
pulumi env open <org>/<env> # View resolved values
pulumi env run <org>/<env> -- <command> # Run with env vars
pulumi env version tag <org>/<env> <tag> # Tag version
# Pulumi Commands
pulumi new go # New project
pulumi config env add <org>/<env> # Link ESC environment
go build -o $(basename $(pwd)) # Build Go binary
pulumi preview # Preview changes
pulumi up # Deploy
pulumi stack output # View outputs
pulumi destroy # Tear down
Go-Specific Considerations
Module Management
# Initialize Go modules
go mod init myproject
# Add Pulumi dependencies
go get github.com/pulumi/pulumi/sdk/v3
go get github.com/pulumi/pulumi-aws/sdk/v6
# Update dependencies
go mod tidy
Building
# Build before running Pulumi
go build -o $(basename $(pwd))
# Or let Pulumi build automatically (slower)
pulumi up
References
- references/pulumi-esc.md - ESC patterns and commands
- references/pulumi-patterns.md - Common infrastructure patterns
- references/pulumi-go.md - Go-specific guidance
- references/pulumi-best-practices-aws.md - AWS best practices
- references/pulumi-best-practices-azure.md - Azure best practices
- references/pulumi-best-practices-gcp.md - GCP best practices
# Supported AI Coding Agents
This skill is compatible with the SKILL.md standard and works with all major AI coding agents:
Learn more about the SKILL.md standard and how to use these skills with your preferred AI coding agent.