0
# Asset Management
1
2
Pulumi's asset system enables packaging and deployment of files, directories, and remote content as part of your infrastructure resources, commonly used for Lambda functions, container images, and configuration files.
3
4
## Core Asset Classes
5
6
```typescript { .api }
7
abstract class Asset {
8
readonly __pulumiAsset: boolean;
9
}
10
11
abstract class Archive {
12
readonly __pulumiArchive: boolean;
13
}
14
15
// File-based assets
16
class FileAsset extends Asset {
17
constructor(path: string);
18
readonly path: string;
19
}
20
21
class StringAsset extends Asset {
22
constructor(text: string);
23
readonly text: string;
24
}
25
26
class RemoteAsset extends Asset {
27
constructor(uri: string);
28
readonly uri: string;
29
}
30
31
// Archive types
32
class FileArchive extends Archive {
33
constructor(path: string);
34
readonly path: string;
35
}
36
37
class AssetArchive extends Archive {
38
constructor(assets: AssetMap);
39
readonly assets: AssetMap;
40
}
41
42
class RemoteArchive extends Archive {
43
constructor(uri: string);
44
readonly uri: string;
45
}
46
47
type AssetMap = {[name: string]: Asset | Archive};
48
```
49
50
## Usage Examples
51
52
### File Assets
53
54
```typescript
55
import * as pulumi from "@pulumi/pulumi";
56
import * as aws from "@pulumi/aws";
57
58
// Single file asset
59
const configFile = new pulumi.asset.FileAsset("./config/app.json");
60
61
// Use in S3 bucket object
62
const configObject = new aws.s3.BucketObject("app-config", {
63
bucket: bucket.id,
64
key: "config/app.json",
65
source: configFile,
66
contentType: "application/json",
67
});
68
69
// String-based asset for generated content
70
const indexHtml = new pulumi.asset.StringAsset(`
71
<!DOCTYPE html>
72
<html>
73
<head>
74
<title>My App</title>
75
</head>
76
<body>
77
<h1>Welcome to My App</h1>
78
<p>Environment: ${config.require("environment")}</p>
79
</body>
80
</html>
81
`);
82
83
const indexObject = new aws.s3.BucketObject("index", {
84
bucket: bucket.id,
85
key: "index.html",
86
source: indexHtml,
87
contentType: "text/html",
88
});
89
```
90
91
### Archive Assets
92
93
```typescript
94
import * as pulumi from "@pulumi/pulumi";
95
import * as aws from "@pulumi/aws";
96
97
// Directory archive for Lambda function
98
const lambdaCode = new pulumi.asset.FileArchive("./lambda-function");
99
100
const lambdaFunction = new aws.lambda.Function("my-function", {
101
runtime: "nodejs18.x",
102
code: lambdaCode,
103
handler: "index.handler",
104
role: lambdaRole.arn,
105
});
106
107
// Selective file archive
108
const webAssets = new pulumi.asset.AssetArchive({
109
"index.html": new pulumi.asset.FileAsset("./web/index.html"),
110
"styles.css": new pulumi.asset.FileAsset("./web/styles.css"),
111
"app.js": new pulumi.asset.FileAsset("./web/app.js"),
112
"assets/": new pulumi.asset.FileArchive("./web/assets"),
113
});
114
115
const websiteZip = new aws.s3.BucketObject("website", {
116
bucket: bucket.id,
117
key: "website.zip",
118
source: webAssets,
119
});
120
```
121
122
### Remote Assets
123
124
```typescript
125
import * as pulumi from "@pulumi/pulumi";
126
import * as aws from "@pulumi/aws";
127
128
// Remote file asset
129
const remoteScript = new pulumi.asset.RemoteAsset(
130
"https://raw.githubusercontent.com/myorg/scripts/main/setup.sh"
131
);
132
133
// Remote archive
134
const applicationCode = new pulumi.asset.RemoteArchive(
135
"https://github.com/myorg/myapp/archive/v1.2.3.tar.gz"
136
);
137
138
const lambdaFromRemote = new aws.lambda.Function("remote-function", {
139
runtime: "python3.9",
140
code: applicationCode,
141
handler: "main.handler",
142
role: lambdaRole.arn,
143
});
144
```
145
146
### Dynamic Asset Generation
147
148
```typescript
149
import * as pulumi from "@pulumi/pulumi";
150
import * as aws from "@pulumi/aws";
151
import * as fs from "fs";
152
import * as path from "path";
153
154
// Generate configuration file dynamically
155
const config = new pulumi.Config();
156
const environment = config.require("environment");
157
158
const appConfig = {
159
environment: environment,
160
apiEndpoint: config.require("apiEndpoint"),
161
features: {
162
authentication: config.getBoolean("enableAuth") || false,
163
analytics: config.getBoolean("enableAnalytics") || false,
164
},
165
buildTimestamp: new Date().toISOString(),
166
};
167
168
const configAsset = new pulumi.asset.StringAsset(
169
JSON.stringify(appConfig, null, 2)
170
);
171
172
// Create archive with generated content
173
const deploymentPackage = new pulumi.asset.AssetArchive({
174
"config.json": configAsset,
175
"app/": new pulumi.asset.FileArchive("./src"),
176
"package.json": new pulumi.asset.FileAsset("./package.json"),
177
"yarn.lock": new pulumi.asset.FileAsset("./yarn.lock"),
178
});
179
```
180
181
### Container Image Assets
182
183
```typescript
184
import * as pulumi from "@pulumi/pulumi";
185
import * as aws from "@pulumi/aws";
186
import * as docker from "@pulumi/docker";
187
188
// Build and push container image
189
const repository = new aws.ecr.Repository("app-repo");
190
191
const image = new docker.Image("app-image", {
192
build: {
193
context: "./app",
194
dockerfile: "./app/Dockerfile",
195
},
196
imageName: pulumi.interpolate`${repository.repositoryUrl}:latest`,
197
registry: {
198
server: repository.repositoryUrl,
199
username: "AWS",
200
password: pulumi.output(aws.ecr.getAuthorizationToken({
201
registryId: repository.registryId,
202
})).authorizationToken.apply(token =>
203
Buffer.from(token, "base64").toString().split(":")[1]
204
),
205
},
206
});
207
208
// Use in ECS task definition
209
const taskDefinition = new aws.ecs.TaskDefinition("app-task", {
210
family: "my-app",
211
containerDefinitions: pulumi.jsonStringify([{
212
name: "app",
213
image: image.imageName,
214
memory: 512,
215
essential: true,
216
portMappings: [{
217
containerPort: 8080,
218
hostPort: 8080,
219
}],
220
}]),
221
});
222
```
223
224
### Asset Transformation Patterns
225
226
```typescript
227
import * as pulumi from "@pulumi/pulumi";
228
import * as fs from "fs";
229
import * as path from "path";
230
231
// Template processing function
232
function processTemplate(templatePath: string, variables: Record<string, any>): pulumi.asset.StringAsset {
233
let template = fs.readFileSync(templatePath, "utf8");
234
235
Object.entries(variables).forEach(([key, value]) => {
236
const placeholder = new RegExp(`{{\\s*${key}\\s*}}`, "g");
237
template = template.replace(placeholder, String(value));
238
});
239
240
return new pulumi.asset.StringAsset(template);
241
}
242
243
// Use template processing
244
const config = new pulumi.Config();
245
const nginxConfig = processTemplate("./templates/nginx.conf.template", {
246
serverName: config.require("serverName"),
247
upstreamHost: config.require("upstreamHost"),
248
upstreamPort: config.getNumber("upstreamPort") || 8080,
249
});
250
251
// Bundle multiple processed templates
252
const configBundle = new pulumi.asset.AssetArchive({
253
"nginx.conf": nginxConfig,
254
"mime.types": new pulumi.asset.FileAsset("./config/mime.types"),
255
"ssl/": new pulumi.asset.FileArchive("./ssl-certificates"),
256
});
257
```
258
259
### Conditional Assets
260
261
```typescript
262
import * as pulumi from "@pulumi/pulumi";
263
264
const config = new pulumi.Config();
265
const environment = config.require("environment");
266
267
// Different assets based on environment
268
function getLambdaCode(): pulumi.asset.Archive {
269
if (environment === "development") {
270
// Use local development build
271
return new pulumi.asset.FileArchive("./dist/dev");
272
} else if (environment === "production") {
273
// Use optimized production build
274
return new pulumi.asset.FileArchive("./dist/prod");
275
} else {
276
// Use pre-built artifact from CI/CD
277
const artifactUrl = config.require("artifactUrl");
278
return new pulumi.asset.RemoteArchive(artifactUrl);
279
}
280
}
281
282
const lambdaFunction = new aws.lambda.Function("app-function", {
283
runtime: "nodejs18.x",
284
code: getLambdaCode(),
285
handler: "index.handler",
286
role: lambdaRole.arn,
287
environment: {
288
variables: {
289
NODE_ENV: environment,
290
},
291
},
292
});
293
```
294
295
### Asset Validation and Helpers
296
297
```typescript
298
import * as pulumi from "@pulumi/pulumi";
299
import * as fs from "fs";
300
import * as path from "path";
301
302
// Utility function to validate assets exist
303
function validateAssetPath(assetPath: string): string {
304
const fullPath = path.resolve(assetPath);
305
if (!fs.existsSync(fullPath)) {
306
throw new Error(`Asset path does not exist: ${fullPath}`);
307
}
308
return assetPath;
309
}
310
311
// Helper for creating archives with validation
312
function createValidatedArchive(paths: Record<string, string>): pulumi.asset.AssetArchive {
313
const assets: Record<string, pulumi.asset.Asset | pulumi.asset.Archive> = {};
314
315
Object.entries(paths).forEach(([key, assetPath]) => {
316
validateAssetPath(assetPath);
317
318
const stats = fs.statSync(assetPath);
319
if (stats.isDirectory()) {
320
assets[key] = new pulumi.asset.FileArchive(assetPath);
321
} else {
322
assets[key] = new pulumi.asset.FileAsset(assetPath);
323
}
324
});
325
326
return new pulumi.asset.AssetArchive(assets);
327
}
328
329
// Usage with validation
330
const applicationBundle = createValidatedArchive({
331
"src/": "./src",
332
"package.json": "./package.json",
333
"config.json": "./config/production.json",
334
});
335
```
336
337
## Best Practices
338
339
- Use `FileArchive` for packaging entire directories (Lambda functions, web assets)
340
- Use `StringAsset` for generated configuration files and templates
341
- Validate asset paths exist before creating assets
342
- Use remote assets for artifacts from CI/CD pipelines
343
- Consider asset size limits for target services (e.g., Lambda 50MB limit)
344
- Use appropriate content types for S3 objects
345
- Cache remote assets locally during development when possible
346
- Use consistent directory structures for deployable assets
347
- Include only necessary files in archives to minimize deployment size
348
- Use environment-specific asset selection patterns for different deployment stages