Skip to content

Commit 14d7d65

Browse files
authored
Chore: Static site & RDS improvements (#5)
* Make domain and hostedZoneId arguments optional * Fetch aws linux arm ami instead of hardcoding ami id * Update rds postgres version to 15.x * Install @pulumi/random package * Autogenerate rds password if not specified
1 parent 36b8d11 commit 14d7d65

File tree

8 files changed

+80
-36
lines changed

8 files changed

+80
-36
lines changed

README.md

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ type DatabaseService = {
104104
serviceName: string;
105105
dbName: pulumi.Input<string>;
106106
username: pulumi.Input<string>;
107-
password: pulumi.Input<string>;
107+
password?: pulumi.Input<string>;
108108
applyImmediately?: pulumi.Input<boolean>;
109109
skipFinalSnapshot?: pulumi.Input<boolean>;
110110
allocatedStorage?: pulumi.Input<number>;
@@ -129,7 +129,7 @@ export type RedisService = {
129129
export type StaticSiteService = {
130130
type: 'STATIC_SITE';
131131
serviceName: string;
132-
domain: pulumi.Input<string>;
132+
domain?: pulumi.Input<string>;
133133
tags?: pulumi.Input<{
134134
[key: string]: pulumi.Input<string>;
135135
}>;
@@ -268,8 +268,8 @@ new Database(name: string, args: DatabaseArgs, opts?: pulumi.CustomResourceOptio
268268
type DatabaseArgs = {
269269
dbName: pulumi.Input<string>;
270270
username: pulumi.Input<string>;
271-
password: pulumi.Input<string>;
272271
vpc: awsx.ec2.Vpc;
272+
password?: pulumi.Input<string>;
273273
applyImmediately?: pulumi.Input<boolean>;
274274
skipFinalSnapshot?: pulumi.Input<boolean>;
275275
allocatedStorage?: pulumi.Input<number>;
@@ -281,6 +281,7 @@ type DatabaseArgs = {
281281
};
282282
```
283283

284+
If the password is not specified it will be autogenerated.
284285
The database password is stored as a secret inside AWS Secret Manager.
285286
The secret will be available on the `Database` resource as `passwordSecret`.
286287

@@ -353,8 +354,8 @@ new StaticSite(name: string, args: StaticSiteArgs, opts?: pulumi.ComponentResour
353354

354355
```ts
355356
type StaticSiteArgs = {
356-
domain: pulumi.Input<string>;
357-
hostedZoneId: pulumi.Input<string>;
357+
domain?: pulumi.Input<string>;
358+
hostedZoneId?: pulumi.Input<string>;
358359
tags?: pulumi.Input<{
359360
[key: string]: pulumi.Input<string>;
360361
}>;

package-lock.json

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
"@pulumi/aws": "^5.0.0",
3636
"@pulumi/awsx": "^1.0.0",
3737
"@pulumi/pulumi": "^3.0.0",
38+
"@pulumi/random": "^4.14.0",
3839
"@upstash/pulumi": "^0.2.0"
3940
},
4041
"devDependencies": {

src/components/database.ts

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import * as aws from '@pulumi/aws';
22
import * as awsx from '@pulumi/awsx';
33
import * as pulumi from '@pulumi/pulumi';
4+
import * as random from '@pulumi/random';
45
import { commonTags } from '../constants';
56

67
export type DatabaseArgs = {
@@ -12,15 +13,15 @@ export type DatabaseArgs = {
1213
* Username for the master DB user.
1314
*/
1415
username: pulumi.Input<string>;
15-
/**
16-
* Password for the master DB user.
17-
* The value will be stored as a secret in AWS Secret Manager.
18-
*/
19-
password: pulumi.Input<string>;
2016
/**
2117
* The awsx.ec2.Vpc resource.
2218
*/
2319
vpc: awsx.ec2.Vpc;
20+
/**
21+
* Password for the master DB user. If not specified it will be autogenerated.
22+
* The value will be stored as a secret in AWS Secret Manager.
23+
*/
24+
password?: pulumi.Input<string>;
2425
/**
2526
* Specifies whether any database modifications are applied immediately, or during the next maintenance window. Default is false.
2627
*/
@@ -74,12 +75,13 @@ export class Database extends pulumi.ComponentResource {
7475

7576
this.name = name;
7677

77-
const { vpc, password } = args;
78+
const { vpc } = args;
7879
this.dbSubnetGroup = this.createSubnetGroup({ vpc });
7980
this.dbSecurityGroup = this.createSecurityGroup({ vpc });
8081
this.kms = this.createEncryptionKey();
81-
this.passwordSecret = this.createPasswordSecret({ password });
82-
this.instance = this.createDatabaseInstance(args);
82+
const { instance, passwordSecret } = this.createDatabaseInstance(args);
83+
this.instance = instance;
84+
this.passwordSecret = passwordSecret;
8385

8486
this.registerOutputs();
8587
}
@@ -161,18 +163,28 @@ export class Database extends pulumi.ComponentResource {
161163
private createDatabaseInstance(args: DatabaseArgs) {
162164
const argsWithDefaults = Object.assign({}, defaults, args);
163165
const stack = pulumi.getStack();
166+
const password =
167+
argsWithDefaults.password ||
168+
new random.RandomPassword(`${this.name}-db-password`, {
169+
length: 16,
170+
overrideSpecial: '_%$',
171+
special: true,
172+
}).result;
173+
174+
const passwordSecret = this.createPasswordSecret({ password });
175+
164176
const instance = new aws.rds.Instance(
165177
`${this.name}-rds`,
166178
{
167179
identifierPrefix: `${this.name}-`,
168180
engine: 'postgres',
169-
engineVersion: '14.9',
181+
engineVersion: '15.3',
170182
allocatedStorage: argsWithDefaults.allocatedStorage,
171183
maxAllocatedStorage: argsWithDefaults.maxAllocatedStorage,
172184
instanceClass: argsWithDefaults.instanceClass,
173185
dbName: argsWithDefaults.dbName,
174186
username: argsWithDefaults.username,
175-
password: argsWithDefaults.password,
187+
password,
176188
dbSubnetGroupName: this.dbSubnetGroup.name,
177189
vpcSecurityGroupIds: [this.dbSecurityGroup.id],
178190
storageEncrypted: true,
@@ -189,6 +201,6 @@ export class Database extends pulumi.ComponentResource {
189201
},
190202
{ parent: this },
191203
);
192-
return instance;
204+
return { instance, passwordSecret };
193205
}
194206
}

src/components/ec2-ssm-connect.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,23 @@
11
import * as pulumi from '@pulumi/pulumi';
22
import * as aws from '@pulumi/aws';
33
import * as awsx from '@pulumi/awsx';
4-
import { Ec2AMI, commonTags } from '../constants';
4+
import { commonTags } from '../constants';
55

66
const config = new pulumi.Config('aws');
77
const awsRegion = config.require('region');
88

9+
const AmazonLinux2023_ARM_EC2_AMI = aws.ec2.getAmiOutput({
10+
filters: [
11+
{ name: 'architecture', values: ['arm64'] },
12+
{ name: 'root-device-type', values: ['ebs'] },
13+
{ name: 'virtualization-type', values: ['hvm'] },
14+
{ name: 'ena-support', values: ['true'] },
15+
],
16+
owners: ['amazon'],
17+
nameRegex: 'al2023-ami-2023.2.20231030.1-kernel-6.1-arm64',
18+
mostRecent: true,
19+
});
20+
921
export type Ec2SSMConnectArgs = {
1022
vpc: awsx.ec2.Vpc;
1123
tags?: pulumi.Input<{
@@ -96,7 +108,7 @@ export class Ec2SSMConnect extends pulumi.ComponentResource {
96108
this.ec2 = new aws.ec2.Instance(
97109
`${name}-ec2`,
98110
{
99-
ami: Ec2AMI.AmazonLinux2023.ARM,
111+
ami: AmazonLinux2023_ARM_EC2_AMI.id,
100112
associatePublicIpAddress: false,
101113
instanceType: 't4g.nano',
102114
iamInstanceProfile: ssmProfile.name,

src/components/project.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,6 @@ export class Project extends pulumi.ComponentResource {
170170

171171
private createStaticSiteService(options: StaticSiteService) {
172172
const { serviceName, ...staticSiteOptions } = options;
173-
if (!this.hostedZoneId) throw new MissingHostedZoneId(options.type);
174173
const service = new StaticSite(
175174
serviceName,
176175
{

src/components/static-site.ts

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,11 @@ export type StaticSiteArgs = {
88
* The domain which will be used to access the static site.
99
* The domain or subdomain must belong to the provided hostedZone.
1010
*/
11-
domain: pulumi.Input<string>;
11+
domain?: pulumi.Input<string>;
1212
/**
1313
* The ID of the hosted zone.
1414
*/
15-
hostedZoneId: pulumi.Input<string>;
15+
hostedZoneId?: pulumi.Input<string>;
1616
/**
1717
* A map of tags to assign to the resource.
1818
*/
@@ -23,7 +23,7 @@ export type StaticSiteArgs = {
2323

2424
export class StaticSite extends pulumi.ComponentResource {
2525
name: string;
26-
certificate: AcmCertificate;
26+
certificate?: AcmCertificate;
2727
bucket: aws.s3.Bucket;
2828
cloudfront: aws.cloudfront.Distribution;
2929

@@ -36,18 +36,28 @@ export class StaticSite extends pulumi.ComponentResource {
3636

3737
this.name = name;
3838
const { domain, hostedZoneId, tags } = args;
39-
this.certificate = this.createTlsCertificate({ domain, hostedZoneId });
39+
const hasCustomDomain = domain && hostedZoneId;
40+
if (domain && !hostedZoneId) {
41+
throw new Error(
42+
'StaticSite:hostedZoneId must be provided when the domain is specified',
43+
);
44+
}
45+
if (hasCustomDomain) {
46+
this.certificate = this.createTlsCertificate({ domain, hostedZoneId });
47+
}
4048
this.bucket = this.createPublicBucket({ tags });
4149
this.cloudfront = this.createCloudfrontDistribution({ domain, tags });
42-
this.createDnsRecord({ domain, hostedZoneId });
50+
if (hasCustomDomain) {
51+
this.createDnsRecord({ domain, hostedZoneId });
52+
}
4353

4454
this.registerOutputs();
4555
}
4656

4757
private createTlsCertificate({
4858
domain,
4959
hostedZoneId,
50-
}: Pick<StaticSiteArgs, 'domain' | 'hostedZoneId'>) {
60+
}: Pick<Required<StaticSiteArgs>, 'domain' | 'hostedZoneId'>) {
5161
const certificate = new AcmCertificate(
5262
`${domain}-acm-certificate`,
5363
{
@@ -120,14 +130,20 @@ export class StaticSite extends pulumi.ComponentResource {
120130
{
121131
enabled: true,
122132
defaultRootObject: 'index.html',
123-
aliases: [domain],
133+
...(domain && { aliases: [domain] }),
124134
isIpv6Enabled: true,
125135
waitForDeployment: true,
126136
httpVersion: 'http2and3',
127137
viewerCertificate: {
128-
acmCertificateArn: this.certificate.certificate.arn,
129-
sslSupportMethod: 'sni-only',
130-
minimumProtocolVersion: 'TLSv1.2_2021',
138+
...(this.certificate
139+
? {
140+
acmCertificateArn: this.certificate.certificate.arn,
141+
sslSupportMethod: 'sni-only',
142+
minimumProtocolVersion: 'TLSv1.2_2021',
143+
}
144+
: {
145+
cloudfrontDefaultCertificate: true,
146+
}),
131147
},
132148
origins: [
133149
{
@@ -171,7 +187,7 @@ export class StaticSite extends pulumi.ComponentResource {
171187
private createDnsRecord({
172188
domain,
173189
hostedZoneId,
174-
}: Pick<StaticSiteArgs, 'domain' | 'hostedZoneId'>) {
190+
}: Pick<Required<StaticSiteArgs>, 'domain' | 'hostedZoneId'>) {
175191
const cdnAliasRecord = new aws.route53.Record(
176192
`${this.name}-cdn-route53-record`,
177193
{

src/constants.ts

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,6 @@ export const PredefinedSize = {
2222
},
2323
} as const;
2424

25-
export const Ec2AMI = {
26-
AmazonLinux2023: {
27-
ARM: 'ami-0b40baa8c6b882e6c',
28-
},
29-
};
30-
3125
export const commonTags = {
3226
Env: pulumi.getStack(),
3327
Project: pulumi.getProject(),

0 commit comments

Comments
 (0)