pulumi-stacks
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChinesePulumi Stacks
Pulumi Stacks
Manage multiple environments and configurations with Pulumi stacks for consistent infrastructure across development, staging, and production.
使用Pulumi stacks管理多环境与配置,确保开发、预发布和生产环境的基础设施一致性。
Overview
概述
Pulumi stacks are isolated, independently configurable instances of a Pulumi program. Each stack has its own state, configuration, and resources, enabling you to deploy the same infrastructure code to multiple environments.
Pulumi stacks是Pulumi程序的独立、可单独配置的实例。每个stack都有自己的状态、配置和资源,让你可以将同一基础设施代码部署到多个环境。
Stack Basics
Stack基础
Creating and Selecting Stacks
创建与选择Stacks
bash
undefinedbash
undefinedInitialize a new project
Initialize a new project
pulumi new aws-typescript
pulumi new aws-typescript
Create a new stack
Create a new stack
pulumi stack init dev
pulumi stack init dev
List all stacks
List all stacks
pulumi stack ls
pulumi stack ls
Select a stack
Select a stack
pulumi stack select dev
pulumi stack select dev
Show current stack
Show current stack
pulumi stack
pulumi stack
Remove a stack
Remove a stack
pulumi stack rm dev
undefinedpulumi stack rm dev
undefinedStack Configuration
Stack配置
bash
undefinedbash
undefinedSet configuration values
Set configuration values
pulumi config set aws:region us-east-1
pulumi config set instanceType t3.micro
pulumi config set aws:region us-east-1
pulumi config set instanceType t3.micro
Set secret values (encrypted)
Set secret values (encrypted)
pulumi config set --secret dbPassword mySecurePassword123
pulumi config set --secret dbPassword mySecurePassword123
Get configuration values
Get configuration values
pulumi config get aws:region
pulumi config get aws:region
List all configuration
List all configuration
pulumi config
pulumi config
Remove configuration
Remove configuration
pulumi config rm instanceType
undefinedpulumi config rm instanceType
undefinedStack Configuration Files
Stack配置文件
Pulumi.yaml (Project File)
Pulumi.yaml(项目文件)
yaml
name: my-infrastructure
user-invocable: false
runtime: nodejs
description: Multi-environment infrastructure
config:
aws:region:
description: AWS region for deployment
default: us-east-1
instanceType:
description: EC2 instance type
default: t3.micro
environment:
description: Environment nameyaml
name: my-infrastructure
user-invocable: false
runtime: nodejs
description: Multi-environment infrastructure
config:
aws:region:
description: AWS region for deployment
default: us-east-1
instanceType:
description: EC2 instance type
default: t3.micro
environment:
description: Environment namePulumi.dev.yaml (Stack Config)
Pulumi.dev.yaml(Stack配置)
yaml
config:
aws:region: us-east-1
my-infrastructure:instanceType: t3.micro
my-infrastructure:environment: development
my-infrastructure:minSize: "1"
my-infrastructure:maxSize: "3"
my-infrastructure:enableMonitoring: "false"yaml
config:
aws:region: us-east-1
my-infrastructure:instanceType: t3.micro
my-infrastructure:environment: development
my-infrastructure:minSize: "1"
my-infrastructure:maxSize: "3"
my-infrastructure:enableMonitoring: "false"Pulumi.staging.yaml
Pulumi.staging.yaml
yaml
config:
aws:region: us-east-1
my-infrastructure:instanceType: t3.small
my-infrastructure:environment: staging
my-infrastructure:minSize: "2"
my-infrastructure:maxSize: "5"
my-infrastructure:enableMonitoring: "true"yaml
config:
aws:region: us-east-1
my-infrastructure:instanceType: t3.small
my-infrastructure:environment: staging
my-infrastructure:minSize: "2"
my-infrastructure:maxSize: "5"
my-infrastructure:enableMonitoring: "true"Pulumi.prod.yaml
Pulumi.prod.yaml
yaml
config:
aws:region: us-west-2
my-infrastructure:instanceType: t3.medium
my-infrastructure:environment: production
my-infrastructure:minSize: "3"
my-infrastructure:maxSize: "10"
my-infrastructure:enableMonitoring: "true"
my-infrastructure:backupRetention: "30"yaml
config:
aws:region: us-west-2
my-infrastructure:instanceType: t3.medium
my-infrastructure:environment: production
my-infrastructure:minSize: "3"
my-infrastructure:maxSize: "10"
my-infrastructure:enableMonitoring: "true"
my-infrastructure:backupRetention: "30"Reading Configuration in Code
在代码中读取配置
TypeScript Configuration
TypeScript配置
typescript
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
// Get configuration
const config = new pulumi.Config();
const instanceType = config.get("instanceType") || "t3.micro";
const environment = config.require("environment");
const minSize = config.getNumber("minSize") || 1;
const maxSize = config.getNumber("maxSize") || 3;
const enableMonitoring = config.getBoolean("enableMonitoring") || false;
// Get secret
const dbPassword = config.requireSecret("dbPassword");
// Use configuration
const instance = new aws.ec2.Instance("web-server", {
instanceType: instanceType,
ami: "ami-0c55b159cbfafe1f0",
tags: {
Name: `web-server-${environment}`,
Environment: environment,
},
monitoring: enableMonitoring,
});
// Export stack name
export const stackName = pulumi.getStack();
export const instanceId = instance.id;typescript
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
// Get configuration
const config = new pulumi.Config();
const instanceType = config.get("instanceType") || "t3.micro";
const environment = config.require("environment");
const minSize = config.getNumber("minSize") || 1;
const maxSize = config.getNumber("maxSize") || 3;
const enableMonitoring = config.getBoolean("enableMonitoring") || false;
// Get secret
const dbPassword = config.requireSecret("dbPassword");
// Use configuration
const instance = new aws.ec2.Instance("web-server", {
instanceType: instanceType,
ami: "ami-0c55b159cbfafe1f0",
tags: {
Name: `web-server-${environment}`,
Environment: environment,
},
monitoring: enableMonitoring,
});
// Export stack name
export const stackName = pulumi.getStack();
export const instanceId = instance.id;Python Configuration
Python配置
python
import pulumi
import pulumi_aws as awspython
import pulumi
import pulumi_aws as awsGet configuration
Get configuration
config = pulumi.Config()
instance_type = config.get("instanceType") or "t3.micro"
environment = config.require("environment")
min_size = config.get_int("minSize") or 1
max_size = config.get_int("maxSize") or 3
enable_monitoring = config.get_bool("enableMonitoring") or False
config = pulumi.Config()
instance_type = config.get("instanceType") or "t3.micro"
environment = config.require("environment")
min_size = config.get_int("minSize") or 1
max_size = config.get_int("maxSize") or 3
enable_monitoring = config.get_bool("enableMonitoring") or False
Get secret
Get secret
db_password = config.require_secret("dbPassword")
db_password = config.require_secret("dbPassword")
Use configuration
Use configuration
instance = aws.ec2.Instance(
"web-server",
instance_type=instance_type,
ami="ami-0c55b159cbfafe1f0",
tags={
"Name": f"web-server-{environment}",
"Environment": environment,
},
monitoring=enable_monitoring,
)
instance = aws.ec2.Instance(
"web-server",
instance_type=instance_type,
ami="ami-0c55b159cbfafe1f0",
tags={
"Name": f"web-server-{environment}",
"Environment": environment,
},
monitoring=enable_monitoring,
)
Export outputs
Export outputs
pulumi.export("stack_name", pulumi.get_stack())
pulumi.export("instance_id", instance.id)
undefinedpulumi.export("stack_name", pulumi.get_stack())
pulumi.export("instance_id", instance.id)
undefinedEnvironment-Specific Resources
环境特定资源
Conditional Resource Creation
条件式资源创建
typescript
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const config = new pulumi.Config();
const environment = config.require("environment");
const enableHighAvailability = config.getBoolean("enableHA") || false;
// Create VPC
const vpc = new aws.ec2.Vpc("main", {
cidrBlock: "10.0.0.0/16",
tags: {
Name: `vpc-${environment}`,
Environment: environment,
},
});
// Production gets multiple availability zones
const azCount = environment === "production" ? 3 : 1;
const subnets: aws.ec2.Subnet[] = [];
for (let i = 0; i < azCount; i++) {
const subnet = new aws.ec2.Subnet(`subnet-${i}`, {
vpcId: vpc.id,
cidrBlock: `10.0.${i}.0/24`,
availabilityZone: `us-east-1${String.fromCharCode(97 + i)}`,
tags: {
Name: `subnet-${environment}-${i}`,
Environment: environment,
},
});
subnets.push(subnet);
}
// Only create NAT gateway in production
let natGateway: aws.ec2.NatGateway | undefined;
if (environment === "production") {
const eip = new aws.ec2.Eip("nat-eip", {
vpc: true,
});
natGateway = new aws.ec2.NatGateway("nat", {
allocationId: eip.id,
subnetId: subnets[0].id,
tags: {
Name: `nat-${environment}`,
Environment: environment,
},
});
}
// Create RDS with multi-AZ only in production
const db = new aws.rds.Instance("database", {
engine: "postgres",
engineVersion: "14.7",
instanceClass: environment === "production" ? "db.t3.medium" : "db.t3.micro",
allocatedStorage: environment === "production" ? 100 : 20,
dbName: "myapp",
username: "admin",
password: config.requireSecret("dbPassword"),
multiAz: environment === "production",
backupRetentionPeriod: environment === "production" ? 30 : 7,
skipFinalSnapshot: environment !== "production",
tags: {
Name: `db-${environment}`,
Environment: environment,
},
});
export const vpcId = vpc.id;
export const subnetIds = subnets.map(s => s.id);
export const dbEndpoint = db.endpoint;typescript
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const config = new pulumi.Config();
const environment = config.require("environment");
const enableHighAvailability = config.getBoolean("enableHA") || false;
// Create VPC
const vpc = new aws.ec2.Vpc("main", {
cidrBlock: "10.0.0.0/16",
tags: {
Name: `vpc-${environment}`,
Environment: environment,
},
});
// Production gets multiple availability zones
const azCount = environment === "production" ? 3 : 1;
const subnets: aws.ec2.Subnet[] = [];
for (let i = 0; i < azCount; i++) {
const subnet = new aws.ec2.Subnet(`subnet-${i}`, {
vpcId: vpc.id,
cidrBlock: `10.0.${i}.0/24`,
availabilityZone: `us-east-1${String.fromCharCode(97 + i)}`,
tags: {
Name: `subnet-${environment}-${i}`,
Environment: environment,
},
});
subnets.push(subnet);
}
// Only create NAT gateway in production
let natGateway: aws.ec2.NatGateway | undefined;
if (environment === "production") {
const eip = new aws.ec2.Eip("nat-eip", {
vpc: true,
});
natGateway = new aws.ec2.NatGateway("nat", {
allocationId: eip.id,
subnetId: subnets[0].id,
tags: {
Name: `nat-${environment}`,
Environment: environment,
},
});
}
// Create RDS with multi-AZ only in production
const db = new aws.rds.Instance("database", {
engine: "postgres",
engineVersion: "14.7",
instanceClass: environment === "production" ? "db.t3.medium" : "db.t3.micro",
allocatedStorage: environment === "production" ? 100 : 20,
dbName: "myapp",
username: "admin",
password: config.requireSecret("dbPassword"),
multiAz: environment === "production",
backupRetentionPeriod: environment === "production" ? 30 : 7,
skipFinalSnapshot: environment !== "production",
tags: {
Name: `db-${environment}`,
Environment: environment,
},
});
export const vpcId = vpc.id;
export const subnetIds = subnets.map(s => s.id);
export const dbEndpoint = db.endpoint;Stack References
Stack引用
Cross-Stack References
跨Stack引用
typescript
// Infrastructure stack (infra/index.ts)
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const vpc = new aws.ec2.Vpc("shared-vpc", {
cidrBlock: "10.0.0.0/16",
tags: {
Name: "shared-vpc",
},
});
const subnet = new aws.ec2.Subnet("shared-subnet", {
vpcId: vpc.id,
cidrBlock: "10.0.1.0/24",
tags: {
Name: "shared-subnet",
},
});
// Export for other stacks
export const vpcId = vpc.id;
export const subnetId = subnet.id;
export const vpcCidr = vpc.cidrBlock;typescript
// Application stack (app/index.ts)
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
// Reference infrastructure stack
const infraStack = new pulumi.StackReference("myorg/infra/prod");
// Get outputs from infrastructure stack
const vpcId = infraStack.getOutput("vpcId");
const subnetId = infraStack.getOutput("subnetId");
// Use referenced values
const securityGroup = new aws.ec2.SecurityGroup("app-sg", {
vpcId: vpcId,
description: "Security group for application",
ingress: [{
protocol: "tcp",
fromPort: 80,
toPort: 80,
cidrBlocks: ["0.0.0.0/0"],
}],
});
const instance = new aws.ec2.Instance("app-server", {
instanceType: "t3.micro",
ami: "ami-0c55b159cbfafe1f0",
subnetId: subnetId,
vpcSecurityGroupIds: [securityGroup.id],
tags: {
Name: "app-server",
},
});
export const instanceIp = instance.publicIp;typescript
// Infrastructure stack (infra/index.ts)
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const vpc = new aws.ec2.Vpc("shared-vpc", {
cidrBlock: "10.0.0.0/16",
tags: {
Name: "shared-vpc",
},
});
const subnet = new aws.ec2.Subnet("shared-subnet", {
vpcId: vpc.id,
cidrBlock: "10.0.1.0/24",
tags: {
Name: "shared-subnet",
},
});
// Export for other stacks
export const vpcId = vpc.id;
export const subnetId = subnet.id;
export const vpcCidr = vpc.cidrBlock;typescript
// Application stack (app/index.ts)
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
// Reference infrastructure stack
const infraStack = new pulumi.StackReference("myorg/infra/prod");
// Get outputs from infrastructure stack
const vpcId = infraStack.getOutput("vpcId");
const subnetId = infraStack.getOutput("subnetId");
// Use referenced values
const securityGroup = new aws.ec2.SecurityGroup("app-sg", {
vpcId: vpcId,
description: "Security group for application",
ingress: [{
protocol: "tcp",
fromPort: 80,
toPort: 80,
cidrBlocks: ["0.0.0.0/0"],
}],
});
const instance = new aws.ec2.Instance("app-server", {
instanceType: "t3.micro",
ami: "ami-0c55b159cbfafe1f0",
subnetId: subnetId,
vpcSecurityGroupIds: [securityGroup.id],
tags: {
Name: "app-server",
},
});
export const instanceIp = instance.publicIp;Stack Reference Commands
Stack引用命令
bash
undefinedbash
undefinedDeploy infrastructure stack first
Deploy infrastructure stack first
cd infra
pulumi stack select prod
pulumi up
cd infra
pulumi stack select prod
pulumi up
Then deploy application stack
Then deploy application stack
cd ../app
pulumi stack select prod
pulumi up
cd ../app
pulumi stack select prod
pulumi up
View outputs from referenced stack
View outputs from referenced stack
pulumi stack output --stack myorg/infra/prod
undefinedpulumi stack output --stack myorg/infra/prod
undefinedStack Outputs
Stack输出
Exporting Stack Outputs
导出Stack输出
typescript
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const config = new pulumi.Config();
const environment = config.require("environment");
// Create resources
const vpc = new aws.ec2.Vpc("main", {
cidrBlock: "10.0.0.0/16",
});
const bucket = new aws.s3.Bucket("app-bucket", {
bucket: `myapp-${environment}-bucket`,
});
const db = new aws.rds.Instance("database", {
engine: "postgres",
instanceClass: "db.t3.micro",
allocatedStorage: 20,
dbName: "myapp",
username: "admin",
password: config.requireSecret("dbPassword"),
skipFinalSnapshot: true,
});
// Export outputs
export const vpcId = vpc.id;
export const vpcCidr = vpc.cidrBlock;
export const bucketName = bucket.id;
export const bucketArn = bucket.arn;
export const dbEndpoint = db.endpoint;
export const dbPort = db.port;
// Export computed values
export const dbConnectionString = pulumi.interpolate`postgresql://admin@${db.endpoint}/myapp`;
// Export stack metadata
export const stackName = pulumi.getStack();
export const projectName = pulumi.getProject();
export const region = aws.getRegion().then(r => r.name);typescript
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const config = new pulumi.Config();
const environment = config.require("environment");
// Create resources
const vpc = new aws.ec2.Vpc("main", {
cidrBlock: "10.0.0.0/16",
});
const bucket = new aws.s3.Bucket("app-bucket", {
bucket: `myapp-${environment}-bucket`,
});
const db = new aws.rds.Instance("database", {
engine: "postgres",
instanceClass: "db.t3.micro",
allocatedStorage: 20,
dbName: "myapp",
username: "admin",
password: config.requireSecret("dbPassword"),
skipFinalSnapshot: true,
});
// Export outputs
export const vpcId = vpc.id;
export const vpcCidr = vpc.cidrBlock;
export const bucketName = bucket.id;
export const bucketArn = bucket.arn;
export const dbEndpoint = db.endpoint;
export const dbPort = db.port;
// Export computed values
export const dbConnectionString = pulumi.interpolate`postgresql://admin@${db.endpoint}/myapp`;
// Export stack metadata
export const stackName = pulumi.getStack();
export const projectName = pulumi.getProject();
export const region = aws.getRegion().then(r => r.name);Accessing Stack Outputs
访问Stack输出
bash
undefinedbash
undefinedView all outputs
View all outputs
pulumi stack output
pulumi stack output
Get specific output
Get specific output
pulumi stack output vpcId
pulumi stack output vpcId
Get output as JSON
Get output as JSON
pulumi stack output --json
pulumi stack output --json
Use in shell scripts
Use in shell scripts
VPC_ID=$(pulumi stack output vpcId)
echo "VPC ID: $VPC_ID"
VPC_ID=$(pulumi stack output vpcId)
echo "VPC ID: $VPC_ID"
Export to environment variables
Export to environment variables
export $(pulumi stack output --json | jq -r 'to_entries[] | "(.key)=(.value)"')
undefinedexport $(pulumi stack output --json | jq -r 'to_entries[] | "(.key)=(.value)"')
undefinedStack Transformations
Stack转换
Global Resource Transformations
全局资源转换
typescript
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const config = new pulumi.Config();
const environment = config.require("environment");
// Register global transformation to add tags
pulumi.runtime.registerStackTransformation((args) => {
if (args.type.startsWith("aws:")) {
args.props.tags = {
...args.props.tags,
Environment: environment,
ManagedBy: "Pulumi",
Stack: pulumi.getStack(),
};
}
return {
props: args.props,
opts: args.opts,
};
});
// All AWS resources automatically get tags
const vpc = new aws.ec2.Vpc("main", {
cidrBlock: "10.0.0.0/16",
// tags will be automatically added by transformation
});
const bucket = new aws.s3.Bucket("data", {
// tags will be automatically added by transformation
});typescript
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const config = new pulumi.Config();
const environment = config.require("environment");
// Register global transformation to add tags
pulumi.runtime.registerStackTransformation((args) => {
if (args.type.startsWith("aws:")) {
args.props.tags = {
...args.props.tags,
Environment: environment,
ManagedBy: "Pulumi",
Stack: pulumi.getStack(),
};
}
return {
props: args.props,
opts: args.opts,
};
});
// All AWS resources automatically get tags
const vpc = new aws.ec2.Vpc("main", {
cidrBlock: "10.0.0.0/16",
// tags will be automatically added by transformation
});
const bucket = new aws.s3.Bucket("data", {
// tags will be automatically added by transformation
});Resource-Specific Transformations
特定资源转换
typescript
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const config = new pulumi.Config();
const environment = config.require("environment");
// Transformation to enforce encryption
const enforceEncryption = (args: pulumi.ResourceTransformationArgs) => {
if (args.type === "aws:s3/bucket:Bucket") {
args.props.serverSideEncryptionConfiguration = {
rule: {
applyServerSideEncryptionByDefault: {
sseAlgorithm: "AES256",
},
},
};
}
if (args.type === "aws:rds/instance:Instance") {
args.props.storageEncrypted = true;
}
return {
props: args.props,
opts: args.opts,
};
};
pulumi.runtime.registerStackTransformation(enforceEncryption);
// Resources will be automatically encrypted
const bucket = new aws.s3.Bucket("data");
const db = new aws.rds.Instance("database", {
engine: "postgres",
instanceClass: "db.t3.micro",
allocatedStorage: 20,
});typescript
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const config = new pulumi.Config();
const environment = config.require("environment");
// Transformation to enforce encryption
const enforceEncryption = (args: pulumi.ResourceTransformationArgs) => {
if (args.type === "aws:s3/bucket:Bucket") {
args.props.serverSideEncryptionConfiguration = {
rule: {
applyServerSideEncryptionByDefault: {
sseAlgorithm: "AES256",
},
},
};
}
if (args.type === "aws:rds/instance:Instance") {
args.props.storageEncrypted = true;
}
return {
props: args.props,
opts: args.opts,
};
};
pulumi.runtime.registerStackTransformation(enforceEncryption);
// Resources will be automatically encrypted
const bucket = new aws.s3.Bucket("data");
const db = new aws.rds.Instance("database", {
engine: "postgres",
instanceClass: "db.t3.micro",
allocatedStorage: 20,
});Stack Tags and Organization
Stack标签与组织
Tagging Strategy
标签策略
typescript
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const config = new pulumi.Config();
const environment = config.require("environment");
const project = pulumi.getProject();
const stack = pulumi.getStack();
// Define common tags
const commonTags = {
Project: project,
Environment: environment,
Stack: stack,
ManagedBy: "Pulumi",
CostCenter: config.get("costCenter") || "engineering",
Owner: config.get("owner") || "platform-team",
};
// Helper function to merge tags
function mergeTags(resourceTags?: { [key: string]: string }): { [key: string]: string } {
return {
...commonTags,
...resourceTags,
};
}
// Use consistent tagging
const vpc = new aws.ec2.Vpc("main", {
cidrBlock: "10.0.0.0/16",
tags: mergeTags({
Name: `vpc-${environment}`,
Type: "network",
}),
});
const bucket = new aws.s3.Bucket("data", {
tags: mergeTags({
Name: `data-${environment}`,
Type: "storage",
Compliance: "required",
}),
});
const db = new aws.rds.Instance("database", {
engine: "postgres",
instanceClass: "db.t3.micro",
allocatedStorage: 20,
tags: mergeTags({
Name: `db-${environment}`,
Type: "database",
BackupRequired: "true",
}),
});typescript
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const config = new pulumi.Config();
const environment = config.require("environment");
const project = pulumi.getProject();
const stack = pulumi.getStack();
// Define common tags
const commonTags = {
Project: project,
Environment: environment,
Stack: stack,
ManagedBy: "Pulumi",
CostCenter: config.get("costCenter") || "engineering",
Owner: config.get("owner") || "platform-team",
};
// Helper function to merge tags
function mergeTags(resourceTags?: { [key: string]: string }): { [key: string]: string } {
return {
...commonTags,
...resourceTags,
};
}
// Use consistent tagging
const vpc = new aws.ec2.Vpc("main", {
cidrBlock: "10.0.0.0/16",
tags: mergeTags({
Name: `vpc-${environment}`,
Type: "network",
}),
});
const bucket = new aws.s3.Bucket("data", {
tags: mergeTags({
Name: `data-${environment}`,
Type: "storage",
Compliance: "required",
}),
});
const db = new aws.rds.Instance("database", {
engine: "postgres",
instanceClass: "db.t3.micro",
allocatedStorage: 20,
tags: mergeTags({
Name: `db-${environment}`,
Type: "database",
BackupRequired: "true",
}),
});Stack Import and Export
Stack导入与导出
Exporting Stack State
导出Stack状态
bash
undefinedbash
undefinedExport stack state to JSON
Export stack state to JSON
pulumi stack export > stack-state.json
pulumi stack export > stack-state.json
Export to file
Export to file
pulumi stack export --file stack-backup.json
pulumi stack export --file stack-backup.json
Export with secrets in plaintext (use carefully!)
Export with secrets in plaintext (use carefully!)
pulumi stack export --show-secrets > stack-with-secrets.json
undefinedpulumi stack export --show-secrets > stack-with-secrets.json
undefinedImporting Stack State
导入Stack状态
bash
undefinedbash
undefinedImport stack state
Import stack state
pulumi stack import --file stack-state.json
pulumi stack import --file stack-state.json
Import from stdin
Import from stdin
cat stack-state.json | pulumi stack import
undefinedcat stack-state.json | pulumi stack import
undefinedStack Migration
Stack迁移
bash
undefinedbash
undefinedExport from old stack
Export from old stack
pulumi stack select old-stack
pulumi stack export --file old-stack.json
pulumi stack select old-stack
pulumi stack export --file old-stack.json
Create and import to new stack
Create and import to new stack
pulumi stack init new-stack
pulumi stack import --file old-stack.json
pulumi stack init new-stack
pulumi stack import --file old-stack.json
Verify resources
Verify resources
pulumi preview
undefinedpulumi preview
undefinedMulti-Region Deployments
多区域部署
Region-Specific Stacks
区域特定Stack
typescript
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const config = new pulumi.Config();
const awsConfig = new pulumi.Config("aws");
const region = awsConfig.require("region");
const environment = config.require("environment");
// Create region-specific resources
const vpc = new aws.ec2.Vpc(`vpc-${region}`, {
cidrBlock: "10.0.0.0/16",
tags: {
Name: `vpc-${environment}-${region}`,
Region: region,
Environment: environment,
},
});
// Create CloudFront distribution in us-east-1
const usEast1Provider = new aws.Provider("us-east-1", {
region: "us-east-1",
});
const certificate = new aws.acm.Certificate("cert", {
domainName: `${environment}.example.com`,
validationMethod: "DNS",
tags: {
Name: `cert-${environment}`,
Environment: environment,
},
}, { provider: usEast1Provider });
// Export region info
export const deploymentRegion = region;
export const vpcId = vpc.id;
export const certificateArn = certificate.arn;typescript
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const config = new pulumi.Config();
const awsConfig = new pulumi.Config("aws");
const region = awsConfig.require("region");
const environment = config.require("environment");
// Create region-specific resources
const vpc = new aws.ec2.Vpc(`vpc-${region}`, {
cidrBlock: "10.0.0.0/16",
tags: {
Name: `vpc-${environment}-${region}`,
Region: region,
Environment: environment,
},
});
// Create CloudFront distribution in us-east-1
const usEast1Provider = new aws.Provider("us-east-1", {
region: "us-east-1",
});
const certificate = new aws.acm.Certificate("cert", {
domainName: `${environment}.example.com`,
validationMethod: "DNS",
tags: {
Name: `cert-${environment}`,
Environment: environment,
},
}, { provider: usEast1Provider });
// Export region info
export const deploymentRegion = region;
export const vpcId = vpc.id;
export const certificateArn = certificate.arn;Multi-Region Stack Configuration
多区域Stack配置
yaml
undefinedyaml
undefinedPulumi.us-east-1-prod.yaml
Pulumi.us-east-1-prod.yaml
config:
aws:region: us-east-1
my-app:environment: production
my-app:isPrimaryRegion: "true"
config:
aws:region: us-east-1
my-app:environment: production
my-app:isPrimaryRegion: "true"
Pulumi.us-west-2-prod.yaml
Pulumi.us-west-2-prod.yaml
config:
aws:region: us-west-2
my-app:environment: production
my-app:isPrimaryRegion: "false"
config:
aws:region: us-west-2
my-app:environment: production
my-app:isPrimaryRegion: "false"
Pulumi.eu-west-1-prod.yaml
Pulumi.eu-west-1-prod.yaml
config:
aws:region: eu-west-1
my-app:environment: production
my-app:isPrimaryRegion: "false"
undefinedconfig:
aws:region: eu-west-1
my-app:environment: production
my-app:isPrimaryRegion: "false"
undefinedStack Policies
Stack策略
Protect Resources
保护资源
typescript
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const config = new pulumi.Config();
const environment = config.require("environment");
// Protect production databases
const db = new aws.rds.Instance("database", {
engine: "postgres",
instanceClass: "db.t3.micro",
allocatedStorage: 20,
tags: {
Name: `db-${environment}`,
},
}, {
protect: environment === "production",
});
// Protect production storage
const bucket = new aws.s3.Bucket("data", {
tags: {
Name: `data-${environment}`,
},
}, {
protect: environment === "production",
});typescript
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const config = new pulumi.Config();
const environment = config.require("environment");
// Protect production databases
const db = new aws.rds.Instance("database", {
engine: "postgres",
instanceClass: "db.t3.micro",
allocatedStorage: 20,
tags: {
Name: `db-${environment}`,
},
}, {
protect: environment === "production",
});
// Protect production storage
const bucket = new aws.s3.Bucket("data", {
tags: {
Name: `data-${environment}`,
},
}, {
protect: environment === "production",
});Retain Resources
保留资源
typescript
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const config = new pulumi.Config();
const environment = config.require("environment");
// Retain production databases on stack deletion
const db = new aws.rds.Instance("database", {
engine: "postgres",
instanceClass: "db.t3.micro",
allocatedStorage: 20,
finalSnapshotIdentifier: environment === "production"
? `final-snapshot-${Date.now()}`
: undefined,
skipFinalSnapshot: environment !== "production",
}, {
retainOnDelete: environment === "production",
});typescript
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const config = new pulumi.Config();
const environment = config.require("environment");
// Retain production databases on stack deletion
const db = new aws.rds.Instance("database", {
engine: "postgres",
instanceClass: "db.t3.micro",
allocatedStorage: 20,
finalSnapshotIdentifier: environment === "production"
? `final-snapshot-${Date.now()}`
: undefined,
skipFinalSnapshot: environment !== "production",
}, {
retainOnDelete: environment === "production",
});Stack Secrets Management
Stack密钥管理
Using Encrypted Secrets
使用加密密钥
bash
undefinedbash
undefinedSet encrypted secrets
Set encrypted secrets
pulumi config set --secret dbPassword mySecurePassword123
pulumi config set --secret apiKey sk_live_abc123xyz789
pulumi config set --secret dbPassword mySecurePassword123
pulumi config set --secret apiKey sk_live_abc123xyz789
View config (secrets are encrypted)
View config (secrets are encrypted)
pulumi config
pulumi config
View secrets in plaintext (use carefully!)
View secrets in plaintext (use carefully!)
pulumi config get dbPassword --show-secrets
undefinedpulumi config get dbPassword --show-secrets
undefinedSecrets in Code
代码中的密钥
typescript
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const config = new pulumi.Config();
// Get secret values
const dbPassword = config.requireSecret("dbPassword");
const apiKey = config.requireSecret("apiKey");
// Use secrets in resources
const db = new aws.rds.Instance("database", {
engine: "postgres",
instanceClass: "db.t3.micro",
allocatedStorage: 20,
username: "admin",
password: dbPassword,
});
// Create SSM parameters from secrets
const dbPasswordParam = new aws.ssm.Parameter("db-password", {
name: "/app/database/password",
type: "SecureString",
value: dbPassword,
});
const apiKeyParam = new aws.ssm.Parameter("api-key", {
name: "/app/api/key",
type: "SecureString",
value: apiKey,
});
// Secrets are encrypted in state
export const connectionString = pulumi.secret(
pulumi.interpolate`postgresql://admin:${dbPassword}@${db.endpoint}/myapp`
);typescript
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const config = new pulumi.Config();
// Get secret values
const dbPassword = config.requireSecret("dbPassword");
const apiKey = config.requireSecret("apiKey");
// Use secrets in resources
const db = new aws.rds.Instance("database", {
engine: "postgres",
instanceClass: "db.t3.micro",
allocatedStorage: 20,
username: "admin",
password: dbPassword,
});
// Create SSM parameters from secrets
const dbPasswordParam = new aws.ssm.Parameter("db-password", {
name: "/app/database/password",
type: "SecureString",
value: dbPassword,
});
const apiKeyParam = new aws.ssm.Parameter("api-key", {
name: "/app/api/key",
type: "SecureString",
value: apiKey,
});
// Secrets are encrypted in state
export const connectionString = pulumi.secret(
pulumi.interpolate`postgresql://admin:${dbPassword}@${db.endpoint}/myapp`
);Stack Refresh and State
Stack刷新与状态
Refresh Stack State
刷新Stack状态
bash
undefinedbash
undefinedRefresh stack to match actual cloud state
Refresh stack to match actual cloud state
pulumi refresh
pulumi refresh
Refresh with auto-approval
Refresh with auto-approval
pulumi refresh --yes
pulumi refresh --yes
Refresh specific resources
Refresh specific resources
pulumi refresh --target urn:pulumi:dev::myapp::aws:s3/bucket:Bucket::my-bucket
pulumi refresh --target urn:pulumi:dev::myapp::aws:s3/bucket:Bucket::my-bucket
Refresh and show diff
Refresh and show diff
pulumi refresh --diff
undefinedpulumi refresh --diff
undefinedStack State Management
Stack状态管理
bash
undefinedbash
undefinedView stack state
View stack state
pulumi stack --show-urns
pulumi stack --show-urns
View specific resource
View specific resource
pulumi stack --show-urns | grep my-bucket
pulumi stack --show-urns | grep my-bucket
Remove resource from state (doesn't delete cloud resource)
Remove resource from state (doesn't delete cloud resource)
pulumi state delete 'urn:pulumi:dev::myapp::aws:s3/bucket:Bucket::my-bucket'
pulumi state delete 'urn:pulumi:dev::myapp::aws:s3/bucket:Bucket::my-bucket'
Rename resource in state
Rename resource in state
pulumi state rename 'urn:pulumi:dev::myapp::aws:s3/bucket:Bucket::old-name'
'urn:pulumi:dev::myapp::aws:s3/bucket:Bucket::new-name'
'urn:pulumi:dev::myapp::aws:s3/bucket:Bucket::new-name'
undefinedpulumi state rename 'urn:pulumi:dev::myapp::aws:s3/bucket:Bucket::old-name'
'urn:pulumi:dev::myapp::aws:s3/bucket:Bucket::new-name'
'urn:pulumi:dev::myapp::aws:s3/bucket:Bucket::new-name'
undefinedWhen to Use This Skill
何时使用该技能
Use the skill when you need to:
pulumi-stacks- Deploy infrastructure to multiple environments (dev, staging, prod)
- Manage environment-specific configurations
- Create isolated instances of the same infrastructure
- Share infrastructure outputs between projects
- Implement multi-region deployments
- Separate infrastructure concerns (networking, databases, applications)
- Manage secrets per environment
- Track infrastructure state per environment
- Implement progressive deployment strategies
- Organize complex infrastructure into manageable units
- Apply environment-specific policies and protections
- Maintain consistent infrastructure across environments
当你需要以下操作时,使用技能:
pulumi-stacks- 将基础设施部署到多环境(开发、预发布、生产)
- 管理环境特定配置
- 创建同一基础设施的独立实例
- 在项目间共享基础设施输出
- 实现多区域部署
- 分离基础设施关注点(网络、数据库、应用)
- 按环境管理密钥
- 按环境跟踪基础设施状态
- 实现渐进式部署策略
- 将复杂基础设施组织为可管理的单元
- 应用环境特定的策略与保护措施
- 保持多环境基础设施的一致性
Best Practices
最佳实践
- Naming Convention: Use consistent stack naming like or
<env>(e.g.,<region>-<env>,prod)us-east-1-prod - Configuration Files: Keep stack config files in version control (except secrets)
- Environment Isolation: Never share state between environments; each environment gets its own stack
- Stack References: Use stack references instead of duplicating infrastructure code
- Secrets Management: Always use flag for sensitive values
--secret - Progressive Deployment: Deploy to dev first, then staging, finally production
- State Backups: Regularly export stack state for disaster recovery
- Resource Protection: Enable option for critical production resources
protect - Tagging Strategy: Apply consistent tags across all environments for cost tracking
- Stack Outputs: Export all values needed by other stacks or external systems
- Configuration Validation: Validate configuration values before creating resources
- Environment Parity: Keep environments as similar as possible, differing only in scale
- Automation: Use CI/CD pipelines for stack deployments
- Documentation: Document stack dependencies and required configuration
- State Encryption: Use encrypted state backends for sensitive infrastructure
- 命名规范:使用一致的Stack命名,如或
<env>(例如:<region>-<env>、prod)us-east-1-prod - 配置文件:将Stack配置文件存入版本控制(密钥除外)
- 环境隔离:绝不在环境间共享状态;每个环境使用独立的Stack
- Stack引用:使用Stack引用而非重复基础设施代码
- 密钥管理:对敏感值始终使用标志
--secret - 渐进式部署:先部署到开发环境,再到预发布环境,最后到生产环境
- 状态备份:定期导出Stack状态以用于灾难恢复
- 资源保护:为关键生产资源启用选项
protect - 标签策略:在所有环境中应用一致的标签以进行成本跟踪
- Stack输出:导出所有其他Stack或外部系统所需的值
- 配置验证:在创建资源前验证配置值
- 环境一致性:保持环境尽可能相似,仅在规模上有所不同
- 自动化:使用CI/CD流水线进行Stack部署
- 文档化:记录Stack依赖关系与所需配置
- 状态加密:对敏感基础设施使用加密的状态后端
Common Pitfalls
常见误区
- Hardcoded Values: Hardcoding environment-specific values instead of using configuration
- Shared State: Attempting to share stack state between environments
- Missing Config: Deploying to new stack without setting required configuration
- Unencrypted Secrets: Storing secrets as plain text in configuration
- Inconsistent Naming: Using different naming conventions across stacks
- Broken References: Stack references that point to non-existent stacks or outputs
- Missing Exports: Not exporting values needed by dependent stacks
- Config Drift: Manual changes to config files not reflected in version control
- No Resource Protection: Forgetting to protect critical production resources
- Stack Sprawl: Creating too many stacks without clear organization
- Missing Validation: Not validating configuration before deployment
- Circular Dependencies: Creating circular stack references
- No Backup Strategy: Not exporting stack state for disaster recovery
- Environment Differences: Production significantly different from other environments
- Poor Secret Management: Checking encrypted secrets into public repositories without proper key management
- 硬编码值:使用硬编码的环境特定值而非配置
- 共享状态:尝试在环境间共享Stack状态
- 缺失配置:在未设置必要配置的情况下部署到新Stack
- 未加密密钥:将密钥以明文形式存储在配置中
- 命名不一致:在Stack间使用不同的命名规范
- 无效引用:指向不存在的Stack或输出的Stack引用
- 缺失导出:未导出依赖Stack所需的值
- 配置漂移:配置文件的手动更改未反映在版本控制中
- 未保护资源:忘记保护关键生产资源
- Stack泛滥:创建过多Stack而缺乏清晰的组织
- 缺失验证:部署前未验证配置
- 循环依赖:创建循环的Stack引用
- 无备份策略:未导出Stack状态以用于灾难恢复
- 环境差异过大:生产环境与其他环境差异显著
- 密钥管理不当:在未进行适当密钥管理的情况下将加密密钥存入公共仓库