@studion/infra-code-blocks 0.0.8 → 0.0.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +59 -2
- package/dist/components/database.d.ts +6 -4
- package/dist/components/database.js +16 -1
- package/dist/components/project.d.ts +2 -1
- package/dist/components/project.js +3 -2
- package/dist/components/redis.d.ts +3 -0
- package/dist/components/redis.js +11 -0
- package/dist/components/static-site.js +0 -1
- package/dist/components/web-server.d.ts +8 -1
- package/dist/components/web-server.js +22 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -92,7 +92,7 @@ type DatabaseService = {
|
|
|
92
92
|
serviceName: string;
|
|
93
93
|
dbName: pulumi.Input<string>;
|
|
94
94
|
username: pulumi.Input<string>;
|
|
95
|
-
password
|
|
95
|
+
password?: pulumi.Input<string>;
|
|
96
96
|
applyImmediately?: pulumi.Input<boolean>;
|
|
97
97
|
skipFinalSnapshot?: pulumi.Input<boolean>;
|
|
98
98
|
allocatedStorage?: pulumi.Input<number>;
|
|
@@ -131,6 +131,7 @@ export type WebServerService = {
|
|
|
131
131
|
environment?:
|
|
132
132
|
| aws.ecs.KeyValuePair[]
|
|
133
133
|
| ((services: Services) => aws.ecs.KeyValuePair[]);
|
|
134
|
+
secrets?: aws.ecs.Secret[] | ((services: Services) => aws.ecs.Secret[]);
|
|
134
135
|
image: pulumi.Input<string>;
|
|
135
136
|
port: pulumi.Input<number>;
|
|
136
137
|
domain: pulumi.Input<string>;
|
|
@@ -181,6 +182,54 @@ const project = new studion.Project('demo-project', {
|
|
|
181
182
|
});
|
|
182
183
|
```
|
|
183
184
|
|
|
185
|
+
In order to pass sensitive information to the container use `secrets` instead of `environment`. AWS will fetch values from
|
|
186
|
+
Secret Manager based on arn that is provided for the `valueFrom` field.
|
|
187
|
+
|
|
188
|
+
```ts
|
|
189
|
+
const project = new studion.Project('demo-project', {
|
|
190
|
+
environment: 'DEVELOPMENT',
|
|
191
|
+
services: [
|
|
192
|
+
{
|
|
193
|
+
type: 'WEB_SERVER',
|
|
194
|
+
serviceName: 'api',
|
|
195
|
+
image: imageUri,
|
|
196
|
+
port: 3000,
|
|
197
|
+
domain: 'api.my-domain.com',
|
|
198
|
+
secrets: [
|
|
199
|
+
{ name: 'DB_PASSWORD', valueFrom: 'arn-of-the-secret-manager-secret' },
|
|
200
|
+
],
|
|
201
|
+
},
|
|
202
|
+
],
|
|
203
|
+
});
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
```ts
|
|
207
|
+
const project = new studion.Project('demo-project', {
|
|
208
|
+
environment: 'DEVELOPMENT',
|
|
209
|
+
services: [
|
|
210
|
+
{
|
|
211
|
+
type: 'REDIS',
|
|
212
|
+
serviceName: 'redis',
|
|
213
|
+
dbName: 'test-db',
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
type: 'WEB_SERVER',
|
|
217
|
+
serviceName: 'api',
|
|
218
|
+
image: imageUri,
|
|
219
|
+
port: 3000,
|
|
220
|
+
domain: 'api.my-domain.com',
|
|
221
|
+
secrets: (services: Services) => {
|
|
222
|
+
const redisServiceName = 'redis';
|
|
223
|
+
const redis = services[redisServiceName];
|
|
224
|
+
return [
|
|
225
|
+
{ name: 'REDIS_PASSWORD', valueFrom: redis.passwordSecret.arn },
|
|
226
|
+
];
|
|
227
|
+
},
|
|
228
|
+
},
|
|
229
|
+
],
|
|
230
|
+
});
|
|
231
|
+
```
|
|
232
|
+
|
|
184
233
|
### Database
|
|
185
234
|
|
|
186
235
|
AWS RDS Postgres instance.
|
|
@@ -207,8 +256,8 @@ new Database(name: string, args: DatabaseArgs, opts?: pulumi.CustomResourceOptio
|
|
|
207
256
|
type DatabaseArgs = {
|
|
208
257
|
dbName: pulumi.Input<string>;
|
|
209
258
|
username: pulumi.Input<string>;
|
|
210
|
-
password: pulumi.Input<string>;
|
|
211
259
|
vpc: awsx.ec2.Vpc;
|
|
260
|
+
password?: pulumi.Input<string>;
|
|
212
261
|
applyImmediately?: pulumi.Input<boolean>;
|
|
213
262
|
skipFinalSnapshot?: pulumi.Input<boolean>;
|
|
214
263
|
allocatedStorage?: pulumi.Input<number>;
|
|
@@ -220,6 +269,10 @@ type DatabaseArgs = {
|
|
|
220
269
|
};
|
|
221
270
|
```
|
|
222
271
|
|
|
272
|
+
If a password is not specified, it will be autogenerated and stored as a secret
|
|
273
|
+
inside AWS Secret Manager. The secret will be available on the `Database` resource
|
|
274
|
+
as `passwordSecret`.
|
|
275
|
+
|
|
223
276
|
### Redis
|
|
224
277
|
|
|
225
278
|
[Upstash](https://upstash.com) Redis instance.
|
|
@@ -261,6 +314,9 @@ interface RedisOptions extends pulumi.ComponentResourceOptions {
|
|
|
261
314
|
}
|
|
262
315
|
```
|
|
263
316
|
|
|
317
|
+
After creating the Redis resource, the `passwordSecret` AWS Secret Manager Secret
|
|
318
|
+
will exist on the resource.
|
|
319
|
+
|
|
264
320
|
### Static Site
|
|
265
321
|
|
|
266
322
|
AWS S3 + Cloudfront static site.
|
|
@@ -331,6 +387,7 @@ export type WebServerArgs = {
|
|
|
331
387
|
maxCount?: pulumi.Input<number>;
|
|
332
388
|
size?: pulumi.Input<Size>;
|
|
333
389
|
environment?: aws.ecs.KeyValuePair[];
|
|
390
|
+
secrets?: aws.ecs.Secret[];
|
|
334
391
|
healtCheckPath?: pulumi.Input<string>;
|
|
335
392
|
taskExecutionRoleInlinePolicies?: pulumi.Input<
|
|
336
393
|
pulumi.Input<RoleInlinePolicy>[]
|
|
@@ -10,14 +10,15 @@ export type DatabaseArgs = {
|
|
|
10
10
|
* Username for the master DB user.
|
|
11
11
|
*/
|
|
12
12
|
username: pulumi.Input<string>;
|
|
13
|
-
/**
|
|
14
|
-
* Password for the master DB user.
|
|
15
|
-
*/
|
|
16
|
-
password: pulumi.Input<string>;
|
|
17
13
|
/**
|
|
18
14
|
* The awsx.ec2.Vpc resource.
|
|
19
15
|
*/
|
|
20
16
|
vpc: awsx.ec2.Vpc;
|
|
17
|
+
/**
|
|
18
|
+
* Password for the master DB user. If not specified, it will be autogenerated
|
|
19
|
+
* and stored as a secret in AWS Secret Manager.
|
|
20
|
+
*/
|
|
21
|
+
password?: pulumi.Input<string>;
|
|
21
22
|
/**
|
|
22
23
|
* Specifies whether any database modifications are applied immediately, or during the next maintenance window. Default is false.
|
|
23
24
|
*/
|
|
@@ -50,5 +51,6 @@ export declare class Database extends pulumi.ComponentResource {
|
|
|
50
51
|
kms: aws.kms.Key;
|
|
51
52
|
dbSubnetGroup: aws.rds.SubnetGroup;
|
|
52
53
|
dbSecurityGroup: aws.ec2.SecurityGroup;
|
|
54
|
+
passwordSecret?: aws.secretsmanager.Secret;
|
|
53
55
|
constructor(name: string, args: DatabaseArgs, opts?: pulumi.ComponentResourceOptions);
|
|
54
56
|
}
|
|
@@ -13,6 +13,8 @@ const defaults = {
|
|
|
13
13
|
class Database extends pulumi.ComponentResource {
|
|
14
14
|
constructor(name, args, opts = {}) {
|
|
15
15
|
super('studion:Database', name, {}, opts);
|
|
16
|
+
const project = pulumi.getProject();
|
|
17
|
+
const stack = pulumi.getStack();
|
|
16
18
|
const argsWithDefaults = Object.assign({}, defaults, args);
|
|
17
19
|
this.dbSubnetGroup = new aws.rds.SubnetGroup(`${name}-subnet-group`, {
|
|
18
20
|
subnetIds: argsWithDefaults.vpc.privateSubnetIds,
|
|
@@ -36,6 +38,19 @@ class Database extends pulumi.ComponentResource {
|
|
|
36
38
|
multiRegion: false,
|
|
37
39
|
enableKeyRotation: true,
|
|
38
40
|
}, { parent: this });
|
|
41
|
+
const password = argsWithDefaults.password ||
|
|
42
|
+
aws.secretsmanager
|
|
43
|
+
.getRandomPasswordOutput()
|
|
44
|
+
.apply(res => res.randomPassword);
|
|
45
|
+
if (!argsWithDefaults.password) {
|
|
46
|
+
this.passwordSecret = new aws.secretsmanager.Secret(`${name}-password-secret`, {
|
|
47
|
+
name: `${stack}/${project}/DatabasePassword`,
|
|
48
|
+
}, { parent: this });
|
|
49
|
+
const passwordSecretValue = new aws.secretsmanager.SecretVersion(`${name}-password-secret-value`, {
|
|
50
|
+
secretId: this.passwordSecret.id,
|
|
51
|
+
secretString: password,
|
|
52
|
+
}, { parent: this, dependsOn: [this.passwordSecret] });
|
|
53
|
+
}
|
|
39
54
|
this.instance = new aws.rds.Instance(`${name}-rds`, {
|
|
40
55
|
identifier: name,
|
|
41
56
|
engine: 'postgres',
|
|
@@ -45,7 +60,7 @@ class Database extends pulumi.ComponentResource {
|
|
|
45
60
|
instanceClass: argsWithDefaults.instanceClass,
|
|
46
61
|
dbName: argsWithDefaults.dbName,
|
|
47
62
|
username: argsWithDefaults.username,
|
|
48
|
-
password
|
|
63
|
+
password,
|
|
49
64
|
dbSubnetGroupName: this.dbSubnetGroup.name,
|
|
50
65
|
vpcSecurityGroupIds: [this.dbSecurityGroup.id],
|
|
51
66
|
storageEncrypted: true,
|
|
@@ -27,7 +27,8 @@ export type StaticSiteService = {
|
|
|
27
27
|
export type WebServerService = {
|
|
28
28
|
type: 'WEB_SERVER';
|
|
29
29
|
environment?: aws.ecs.KeyValuePair[] | ((services: Services) => aws.ecs.KeyValuePair[]);
|
|
30
|
-
|
|
30
|
+
secrets?: aws.ecs.Secret[] | ((services: Services) => aws.ecs.Secret[]);
|
|
31
|
+
} & ServiceArgs & Omit<WebServerArgs, 'cluster' | 'vpc' | 'hostedZoneId' | 'environment' | 'secrets'>;
|
|
31
32
|
export type ProjectArgs = {
|
|
32
33
|
services: (DatabaseService | RedisService | StaticSiteService | WebServerService)[];
|
|
33
34
|
hostedZoneId?: pulumi.Input<string>;
|
|
@@ -110,11 +110,12 @@ class Project extends pulumi.ComponentResource {
|
|
|
110
110
|
return;
|
|
111
111
|
if (!this.hostedZoneId)
|
|
112
112
|
throw new MissingHostedZoneId(options.type);
|
|
113
|
-
const { serviceName, environment } = options, ecsOptions = __rest(options, ["serviceName", "environment"]);
|
|
113
|
+
const { serviceName, environment, secrets } = options, ecsOptions = __rest(options, ["serviceName", "environment", "secrets"]);
|
|
114
114
|
const parsedEnv = typeof environment === 'function'
|
|
115
115
|
? environment(this.services)
|
|
116
116
|
: environment;
|
|
117
|
-
const
|
|
117
|
+
const parsedSecrets = typeof secrets === 'function' ? secrets(this.services) : secrets;
|
|
118
|
+
const service = new web_server_1.WebServer(serviceName, Object.assign(Object.assign({}, ecsOptions), { cluster: this.cluster, vpc: this.vpc, hostedZoneId: this.hostedZoneId, environment: parsedEnv, secrets: parsedSecrets }), { parent: this });
|
|
118
119
|
this.services[options.serviceName] = service;
|
|
119
120
|
}
|
|
120
121
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as pulumi from '@pulumi/pulumi';
|
|
2
2
|
import * as upstash from '@upstash/pulumi';
|
|
3
|
+
import * as aws from '@pulumi/aws';
|
|
3
4
|
export type RedisArgs = {
|
|
4
5
|
/**
|
|
5
6
|
* Redis database name.
|
|
@@ -15,5 +16,7 @@ export interface RedisOptions extends pulumi.ComponentResourceOptions {
|
|
|
15
16
|
}
|
|
16
17
|
export declare class Redis extends pulumi.ComponentResource {
|
|
17
18
|
instance: upstash.RedisDatabase;
|
|
19
|
+
passwordSecret: aws.secretsmanager.Secret;
|
|
20
|
+
username: string;
|
|
18
21
|
constructor(name: string, args: RedisArgs, opts: RedisOptions);
|
|
19
22
|
}
|
package/dist/components/redis.js
CHANGED
|
@@ -3,12 +3,16 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.Redis = void 0;
|
|
4
4
|
const pulumi = require("@pulumi/pulumi");
|
|
5
5
|
const upstash = require("@upstash/pulumi");
|
|
6
|
+
const aws = require("@pulumi/aws");
|
|
6
7
|
const defaults = {
|
|
7
8
|
region: 'us-east-1',
|
|
8
9
|
};
|
|
9
10
|
class Redis extends pulumi.ComponentResource {
|
|
10
11
|
constructor(name, args, opts) {
|
|
11
12
|
super('studion:Redis', name, {}, opts);
|
|
13
|
+
this.username = 'default';
|
|
14
|
+
const project = pulumi.getProject();
|
|
15
|
+
const stack = pulumi.getStack();
|
|
12
16
|
const argsWithDefaults = Object.assign({}, defaults, args);
|
|
13
17
|
this.instance = new upstash.RedisDatabase(name, {
|
|
14
18
|
databaseName: argsWithDefaults.dbName,
|
|
@@ -16,6 +20,13 @@ class Redis extends pulumi.ComponentResource {
|
|
|
16
20
|
eviction: true,
|
|
17
21
|
tls: true,
|
|
18
22
|
}, { provider: opts.provider, parent: this });
|
|
23
|
+
this.passwordSecret = new aws.secretsmanager.Secret(`${name}-password-secret`, {
|
|
24
|
+
name: `${stack}/${project}/RedisPassword`,
|
|
25
|
+
}, { parent: this, dependsOn: [this.instance] });
|
|
26
|
+
const passwordSecretValue = new aws.secretsmanager.SecretVersion(`${name}-password-secret-value`, {
|
|
27
|
+
secretId: this.passwordSecret.id,
|
|
28
|
+
secretString: this.instance.password,
|
|
29
|
+
}, { parent: this, dependsOn: [this.passwordSecret] });
|
|
19
30
|
this.registerOutputs();
|
|
20
31
|
}
|
|
21
32
|
}
|
|
@@ -12,7 +12,6 @@ class StaticSite extends pulumi.ComponentResource {
|
|
|
12
12
|
hostedZoneId: args.hostedZoneId,
|
|
13
13
|
}, { parent: this });
|
|
14
14
|
const bucket = new aws.s3.Bucket(`${name}-bucket`, {
|
|
15
|
-
bucket: name,
|
|
16
15
|
website: {
|
|
17
16
|
indexDocument: 'index.html',
|
|
18
17
|
errorDocument: 'index.html',
|
|
@@ -61,9 +61,16 @@ export type WebServerArgs = {
|
|
|
61
61
|
*/
|
|
62
62
|
size?: pulumi.Input<Size>;
|
|
63
63
|
/**
|
|
64
|
-
* The environment variables to pass to a container.
|
|
64
|
+
* The environment variables to pass to a container. Don't use this field for
|
|
65
|
+
* sensitive information such as passwords, API keys, etc. For that purpose,
|
|
66
|
+
* please use the `secrets` property.
|
|
67
|
+
* Defaults to [].
|
|
65
68
|
*/
|
|
66
69
|
environment?: aws.ecs.KeyValuePair[];
|
|
70
|
+
/**
|
|
71
|
+
* The secrets to pass to the container. Defaults to [].
|
|
72
|
+
*/
|
|
73
|
+
secrets?: aws.ecs.Secret[];
|
|
67
74
|
/**
|
|
68
75
|
* Path for the health check request. Defaults to "/healtcheck".
|
|
69
76
|
*/
|
|
@@ -26,6 +26,7 @@ const defaults = {
|
|
|
26
26
|
maxCount: 10,
|
|
27
27
|
size: 'small',
|
|
28
28
|
environment: [],
|
|
29
|
+
secrets: [],
|
|
29
30
|
healtCheckPath: '/healtcheck',
|
|
30
31
|
taskExecutionRoleInlinePolicies: [],
|
|
31
32
|
taskRoleInlinePolicies: [],
|
|
@@ -128,6 +129,20 @@ class WebServer extends pulumi.ComponentResource {
|
|
|
128
129
|
},
|
|
129
130
|
],
|
|
130
131
|
}, { parent: this });
|
|
132
|
+
const secretManagerSecretsInlinePolicy = {
|
|
133
|
+
name: `${name}-secret-manager-access`,
|
|
134
|
+
policy: JSON.stringify({
|
|
135
|
+
Version: '2012-10-17',
|
|
136
|
+
Statement: [
|
|
137
|
+
{
|
|
138
|
+
Sid: 'AllowContainerToGetSecretManagerSecrets',
|
|
139
|
+
Effect: 'Allow',
|
|
140
|
+
Action: ['secretsmanager:GetSecretValue'],
|
|
141
|
+
Resource: '*',
|
|
142
|
+
},
|
|
143
|
+
],
|
|
144
|
+
}),
|
|
145
|
+
};
|
|
131
146
|
const taskExecutionRole = new aws.iam.Role(`${name}-ecs-task-exec-role`, {
|
|
132
147
|
name: `${name}-ecs-task-exec-role`,
|
|
133
148
|
assumeRolePolicy,
|
|
@@ -135,7 +150,10 @@ class WebServer extends pulumi.ComponentResource {
|
|
|
135
150
|
'arn:aws:iam::aws:policy/CloudWatchFullAccess',
|
|
136
151
|
'arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryFullAccess',
|
|
137
152
|
],
|
|
138
|
-
inlinePolicies:
|
|
153
|
+
inlinePolicies: [
|
|
154
|
+
secretManagerSecretsInlinePolicy,
|
|
155
|
+
...argsWithDefaults.taskExecutionRoleInlinePolicies,
|
|
156
|
+
],
|
|
139
157
|
}, { parent: this });
|
|
140
158
|
const execCmdInlinePolicy = {
|
|
141
159
|
name: `${name}-ecs-exec`,
|
|
@@ -191,10 +209,11 @@ class WebServer extends pulumi.ComponentResource {
|
|
|
191
209
|
argsWithDefaults.image,
|
|
192
210
|
argsWithDefaults.port,
|
|
193
211
|
argsWithDefaults.environment,
|
|
212
|
+
argsWithDefaults.secrets,
|
|
194
213
|
this.logGroup.name,
|
|
195
214
|
awsRegion,
|
|
196
215
|
])
|
|
197
|
-
.apply(([containerName, image, port, environment, logGroup, region]) => {
|
|
216
|
+
.apply(([containerName, image, port, environment, secrets, logGroup, region,]) => {
|
|
198
217
|
return JSON.stringify([
|
|
199
218
|
{
|
|
200
219
|
readonlyRootFilesystem: false,
|
|
@@ -216,6 +235,7 @@ class WebServer extends pulumi.ComponentResource {
|
|
|
216
235
|
},
|
|
217
236
|
},
|
|
218
237
|
environment,
|
|
238
|
+
secrets,
|
|
219
239
|
},
|
|
220
240
|
]);
|
|
221
241
|
}),
|