0
# Utilities
1
2
Pulumi provides various utility functions and helpers for common operations, type checking, and metadata access in Pulumi programs.
3
4
## Type Utilities
5
6
```typescript { .api }
7
function isInstance<T>(obj: any, name: string): obj is T;
8
function hasTrueBooleanMember(obj: any, memberName: string): boolean;
9
function hasFunctionMember(obj: any, memberName: string): boolean;
10
```
11
12
## Collection Utilities
13
14
```typescript { .api }
15
function values(obj: any): any[];
16
function union<T>(set1: Set<T>, set2: Set<T>): Set<T>;
17
function toObject<T, V>(source: Iterable<T>, keySelector: (item: T) => string, elementSelector?: (item: T) => V): {[key: string]: V};
18
function groupBy<T, V>(source: Iterable<T>, keySelector: (item: T) => string, elementSelector?: (item: T) => V): {[key: string]: V[]};
19
```
20
21
## Metadata Functions
22
23
```typescript { .api }
24
function getOrganization(): string;
25
function getProject(): string;
26
function getStack(): string;
27
function getRootDirectory(): string;
28
```
29
30
## Configuration Constants
31
32
```typescript { .api }
33
const disableResourceReferences: boolean;
34
const errorOutputString: boolean;
35
```
36
37
## Usage Examples
38
39
### Type Checking Utilities
40
41
```typescript
42
import * as pulumi from "@pulumi/pulumi";
43
44
// Safe type checking for Pulumi types across versions
45
function isOutput(obj: any): obj is pulumi.Output<any> {
46
return pulumi.utils.isInstance(obj, "__pulumiOutput");
47
}
48
49
function isResource(obj: any): obj is pulumi.Resource {
50
return pulumi.utils.isInstance(obj, "__pulumiResource");
51
}
52
53
function isAsset(obj: any): obj is pulumi.Asset {
54
return pulumi.utils.isInstance(obj, "__pulumiAsset");
55
}
56
57
// Check for specific members
58
function hasOutputMethods(obj: any): boolean {
59
return pulumi.utils.hasFunctionMember(obj, "apply") &&
60
pulumi.utils.hasFunctionMember(obj, "isKnown");
61
}
62
63
// Check boolean flags
64
function isProtectedResource(obj: any): boolean {
65
return pulumi.utils.hasTrueBooleanMember(obj, "__pulumiProtected");
66
}
67
68
// Usage
69
const bucket = new aws.s3.Bucket("my-bucket");
70
71
if (isResource(bucket)) {
72
console.log("Object is a Pulumi resource");
73
}
74
75
if (isOutput(bucket.id)) {
76
console.log("Bucket ID is an Output");
77
}
78
```
79
80
### Collection Operations
81
82
```typescript
83
import * as pulumi from "@pulumi/pulumi";
84
85
// Convert array to object with key selector
86
const resources = [
87
{ name: "web-server-1", type: "ec2", region: "us-east-1" },
88
{ name: "web-server-2", type: "ec2", region: "us-west-2" },
89
{ name: "db-server-1", type: "rds", region: "us-east-1" },
90
];
91
92
const resourcesByName = pulumi.iterable.toObject(
93
resources,
94
item => item.name,
95
item => item
96
);
97
98
console.log(resourcesByName["web-server-1"]); // { name: "web-server-1", ... }
99
100
// Group resources by type
101
const resourcesByType = pulumi.iterable.groupBy(
102
resources,
103
item => item.type,
104
item => item.name
105
);
106
107
console.log(resourcesByType.ec2); // ["web-server-1", "web-server-2"]
108
console.log(resourcesByType.rds); // ["db-server-1"]
109
110
// Get object values
111
const config = {
112
environment: "production",
113
region: "us-east-1",
114
debug: false,
115
};
116
117
const configValues = pulumi.utils.values(config);
118
console.log(configValues); // ["production", "us-east-1", false]
119
120
// Set operations
121
const set1 = new Set(["a", "b", "c"]);
122
const set2 = new Set(["c", "d", "e"]);
123
const combined = pulumi.utils.union(set1, set2);
124
console.log(Array.from(combined)); // ["a", "b", "c", "d", "e"]
125
```
126
127
### Metadata Access
128
129
```typescript
130
import * as pulumi from "@pulumi/pulumi";
131
132
// Get deployment context information
133
const organization = pulumi.getOrganization();
134
const project = pulumi.getProject();
135
const stack = pulumi.getStack();
136
const rootDir = pulumi.getRootDirectory();
137
138
console.log(`Deploying ${organization}/${project}/${stack}`);
139
console.log(`Project root: ${rootDir}`);
140
141
// Use metadata in resource naming
142
const resourcePrefix = `${project}-${stack}`;
143
144
const bucket = new aws.s3.Bucket(`${resourcePrefix}-assets`, {
145
bucket: `${organization}-${project}-${stack}-assets`,
146
tags: {
147
Project: project,
148
Stack: stack,
149
Organization: organization,
150
},
151
});
152
153
// Create environment-aware configurations
154
function getEnvironmentConfig() {
155
const stackName = pulumi.getStack();
156
157
const configs = {
158
development: {
159
instanceType: "t3.micro",
160
minSize: 1,
161
maxSize: 2,
162
},
163
staging: {
164
instanceType: "t3.small",
165
minSize: 2,
166
maxSize: 4,
167
},
168
production: {
169
instanceType: "t3.medium",
170
minSize: 3,
171
maxSize: 10,
172
},
173
};
174
175
return configs[stackName as keyof typeof configs] || configs.development;
176
}
177
178
const envConfig = getEnvironmentConfig();
179
console.log(`Using configuration for ${pulumi.getStack()}:`, envConfig);
180
```
181
182
### Utility Helper Functions
183
184
```typescript
185
import * as pulumi from "@pulumi/pulumi";
186
187
// Helper for checking resource types
188
function getResourceType(resource: pulumi.Resource): string {
189
if (pulumi.utils.isInstance(resource, "__pulumiCustomResource")) {
190
return "CustomResource";
191
} else if (pulumi.utils.isInstance(resource, "__pulumiComponentResource")) {
192
return "ComponentResource";
193
} else if (pulumi.utils.isInstance(resource, "__pulumiProviderResource")) {
194
return "ProviderResource";
195
}
196
return "Resource";
197
}
198
199
// Helper for extracting output values safely
200
function extractOutputValue<T>(output: pulumi.Output<T>, defaultValue: T): Promise<T> {
201
return output.apply(value => value !== undefined ? value : defaultValue);
202
}
203
204
// Helper for creating resource names with metadata
205
function createResourceName(baseName: string, suffix?: string): string {
206
const project = pulumi.getProject();
207
const stack = pulumi.getStack();
208
209
let name = `${project}-${stack}-${baseName}`;
210
if (suffix) {
211
name += `-${suffix}`;
212
}
213
214
// Ensure name meets resource naming requirements
215
return name.toLowerCase().replace(/[^a-z0-9-]/g, '-');
216
}
217
218
// Helper for validating resource configurations
219
function validateResourceConfig(config: any, required: string[]): void {
220
const missing = required.filter(key => !pulumi.utils.hasTrueBooleanMember(config, key) && !config[key]);
221
222
if (missing.length > 0) {
223
throw new pulumi.RunError(`Missing required configuration: ${missing.join(", ")}`);
224
}
225
}
226
227
// Usage examples
228
const bucket = new aws.s3.Bucket("assets");
229
console.log(`Resource type: ${getResourceType(bucket)}`);
230
231
const bucketId = await extractOutputValue(bucket.id, "default-bucket-id");
232
console.log(`Bucket ID: ${bucketId}`);
233
234
const resourceName = createResourceName("web-server", "primary");
235
console.log(`Generated name: ${resourceName}`);
236
237
validateResourceConfig({ name: "test", enabled: true }, ["name", "enabled"]);
238
```
239
240
### Debugging and Inspection Utilities
241
242
```typescript
243
import * as pulumi from "@pulumi/pulumi";
244
245
// Utility for inspecting resource properties
246
function inspectResource(resource: pulumi.Resource): void {
247
console.log(`Resource URN: ${resource.urn}`);
248
249
if (pulumi.utils.isInstance(resource, "__pulumiCustomResource")) {
250
const customResource = resource as pulumi.CustomResource;
251
customResource.id.apply(id => console.log(`Resource ID: ${id}`));
252
}
253
254
// Check for common properties
255
const resourceAny = resource as any;
256
Object.keys(resourceAny).forEach(key => {
257
if (pulumi.utils.isInstance(resourceAny[key], "__pulumiOutput")) {
258
console.log(`Property ${key} is an Output`);
259
}
260
});
261
}
262
263
// Utility for checking Pulumi state
264
function checkPulumiState(): void {
265
const hasSecrets = Object.keys(process.env).some(key =>
266
key.startsWith('PULUMI_CONFIG_') && key.endsWith('_SECRET')
267
);
268
269
console.log(`Project: ${pulumi.getProject()}`);
270
console.log(`Stack: ${pulumi.getStack()}`);
271
console.log(`Organization: ${pulumi.getOrganization()}`);
272
console.log(`Has secrets: ${hasSecrets}`);
273
}
274
275
// Development helper for listing all outputs
276
function listAllOutputs(resources: pulumi.Resource[]): void {
277
resources.forEach((resource, index) => {
278
console.log(`Resource ${index}: ${getResourceType(resource)}`);
279
280
const resourceAny = resource as any;
281
Object.keys(resourceAny).forEach(key => {
282
if (pulumi.utils.isInstance(resourceAny[key], "__pulumiOutput")) {
283
resourceAny[key].apply((value: any) => {
284
console.log(` ${key}: ${JSON.stringify(value)}`);
285
});
286
}
287
});
288
});
289
}
290
291
// Usage
292
const bucket = new aws.s3.Bucket("debug-bucket");
293
const database = new aws.rds.Instance("debug-db", {
294
engine: "postgres",
295
instanceClass: "db.t3.micro",
296
});
297
298
inspectResource(bucket);
299
checkPulumiState();
300
listAllOutputs([bucket, database]);
301
```
302
303
### Performance and Optimization Helpers
304
305
```typescript
306
import * as pulumi from "@pulumi/pulumi";
307
308
// Helper for batching operations
309
function batchOperations<T, R>(
310
items: T[],
311
batchSize: number,
312
operation: (batch: T[]) => Promise<R[]>
313
): Promise<R[]> {
314
const batches: T[][] = [];
315
316
for (let i = 0; i < items.length; i += batchSize) {
317
batches.push(items.slice(i, i + batchSize));
318
}
319
320
return Promise.all(batches.map(operation)).then(results =>
321
results.flat()
322
);
323
}
324
325
// Helper for memoizing expensive operations
326
function memoize<T extends (...args: any[]) => any>(fn: T): T {
327
const cache = new Map();
328
329
return ((...args: any[]) => {
330
const key = JSON.stringify(args);
331
332
if (cache.has(key)) {
333
return cache.get(key);
334
}
335
336
const result = fn(...args);
337
cache.set(key, result);
338
return result;
339
}) as T;
340
}
341
342
// Memoized expensive configuration calculation
343
const getExpensiveConfig = memoize((stack: string) => {
344
console.log(`Computing expensive config for ${stack}`);
345
// Expensive operation here
346
return {
347
setting1: `value-for-${stack}`,
348
setting2: Math.random(),
349
};
350
});
351
352
// Usage
353
const config1 = getExpensiveConfig(pulumi.getStack()); // Computed
354
const config2 = getExpensiveConfig(pulumi.getStack()); // Cached
355
```
356
357
## Best Practices
358
359
- Use type checking utilities for robust cross-version compatibility
360
- Leverage metadata functions for consistent resource naming
361
- Use collection utilities for data transformation and grouping
362
- Implement proper validation using utility functions
363
- Use memoization for expensive computations
364
- Create reusable helper functions for common patterns
365
- Use debugging utilities during development
366
- Leverage set operations for managing collections
367
- Extract utility functions into shared modules
368
- Document custom utility functions thoroughly
369
- Test utility functions independently
370
- Use TypeScript types with utility functions for better safety