Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ type DatabaseService = {
serviceName: string;
dbName: pulumi.Input<string>;
username: pulumi.Input<string>;
password: pulumi.Input<string>;
password?: pulumi.Input<string>;
applyImmediately?: pulumi.Input<boolean>;
skipFinalSnapshot?: pulumi.Input<boolean>;
allocatedStorage?: pulumi.Input<number>;
Expand All @@ -129,7 +129,7 @@ export type RedisService = {
export type StaticSiteService = {
type: 'STATIC_SITE';
serviceName: string;
domain: pulumi.Input<string>;
domain?: pulumi.Input<string>;
tags?: pulumi.Input<{
[key: string]: pulumi.Input<string>;
}>;
Expand Down Expand Up @@ -268,8 +268,8 @@ new Database(name: string, args: DatabaseArgs, opts?: pulumi.CustomResourceOptio
type DatabaseArgs = {
dbName: pulumi.Input<string>;
username: pulumi.Input<string>;
password: pulumi.Input<string>;
vpc: awsx.ec2.Vpc;
password?: pulumi.Input<string>;
applyImmediately?: pulumi.Input<boolean>;
skipFinalSnapshot?: pulumi.Input<boolean>;
allocatedStorage?: pulumi.Input<number>;
Expand All @@ -281,6 +281,7 @@ type DatabaseArgs = {
};
```

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

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

```ts
type StaticSiteArgs = {
domain: pulumi.Input<string>;
hostedZoneId: pulumi.Input<string>;
domain?: pulumi.Input<string>;
hostedZoneId?: pulumi.Input<string>;
tags?: pulumi.Input<{
[key: string]: pulumi.Input<string>;
}>;
Expand Down
9 changes: 9 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
"@pulumi/aws": "^5.0.0",
"@pulumi/awsx": "^1.0.0",
"@pulumi/pulumi": "^3.0.0",
"@pulumi/random": "^4.14.0",
"@upstash/pulumi": "^0.2.0"
},
"devDependencies": {
Expand Down
34 changes: 23 additions & 11 deletions src/components/database.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as aws from '@pulumi/aws';
import * as awsx from '@pulumi/awsx';
import * as pulumi from '@pulumi/pulumi';
import * as random from '@pulumi/random';
import { commonTags } from '../constants';

export type DatabaseArgs = {
Expand All @@ -12,15 +13,15 @@ export type DatabaseArgs = {
* Username for the master DB user.
*/
username: pulumi.Input<string>;
/**
* Password for the master DB user.
* The value will be stored as a secret in AWS Secret Manager.
*/
password: pulumi.Input<string>;
/**
* The awsx.ec2.Vpc resource.
*/
vpc: awsx.ec2.Vpc;
/**
* Password for the master DB user. If not specified it will be autogenerated.
* The value will be stored as a secret in AWS Secret Manager.
*/
password?: pulumi.Input<string>;
/**
* Specifies whether any database modifications are applied immediately, or during the next maintenance window. Default is false.
*/
Expand Down Expand Up @@ -74,12 +75,13 @@ export class Database extends pulumi.ComponentResource {

this.name = name;

const { vpc, password } = args;
const { vpc } = args;
this.dbSubnetGroup = this.createSubnetGroup({ vpc });
this.dbSecurityGroup = this.createSecurityGroup({ vpc });
this.kms = this.createEncryptionKey();
this.passwordSecret = this.createPasswordSecret({ password });
this.instance = this.createDatabaseInstance(args);
const { instance, passwordSecret } = this.createDatabaseInstance(args);
this.instance = instance;
this.passwordSecret = passwordSecret;

this.registerOutputs();
}
Expand Down Expand Up @@ -161,18 +163,28 @@ export class Database extends pulumi.ComponentResource {
private createDatabaseInstance(args: DatabaseArgs) {
const argsWithDefaults = Object.assign({}, defaults, args);
const stack = pulumi.getStack();
const password =
argsWithDefaults.password ||
new random.RandomPassword(`${this.name}-db-password`, {
length: 16,
overrideSpecial: '_%$',
special: true,
}).result;

const passwordSecret = this.createPasswordSecret({ password });

const instance = new aws.rds.Instance(
`${this.name}-rds`,
{
identifierPrefix: `${this.name}-`,
engine: 'postgres',
engineVersion: '14.9',
engineVersion: '15.3',
allocatedStorage: argsWithDefaults.allocatedStorage,
maxAllocatedStorage: argsWithDefaults.maxAllocatedStorage,
instanceClass: argsWithDefaults.instanceClass,
dbName: argsWithDefaults.dbName,
username: argsWithDefaults.username,
password: argsWithDefaults.password,
password,
dbSubnetGroupName: this.dbSubnetGroup.name,
vpcSecurityGroupIds: [this.dbSecurityGroup.id],
storageEncrypted: true,
Expand All @@ -189,6 +201,6 @@ export class Database extends pulumi.ComponentResource {
},
{ parent: this },
);
return instance;
return { instance, passwordSecret };
}
}
16 changes: 14 additions & 2 deletions src/components/ec2-ssm-connect.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,23 @@
import * as pulumi from '@pulumi/pulumi';
import * as aws from '@pulumi/aws';
import * as awsx from '@pulumi/awsx';
import { Ec2AMI, commonTags } from '../constants';
import { commonTags } from '../constants';

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

const AmazonLinux2023_ARM_EC2_AMI = aws.ec2.getAmiOutput({
filters: [
{ name: 'architecture', values: ['arm64'] },
{ name: 'root-device-type', values: ['ebs'] },
{ name: 'virtualization-type', values: ['hvm'] },
{ name: 'ena-support', values: ['true'] },
],
owners: ['amazon'],
nameRegex: 'al2023-ami-2023.2.20231030.1-kernel-6.1-arm64',
mostRecent: true,
});

export type Ec2SSMConnectArgs = {
vpc: awsx.ec2.Vpc;
tags?: pulumi.Input<{
Expand Down Expand Up @@ -96,7 +108,7 @@ export class Ec2SSMConnect extends pulumi.ComponentResource {
this.ec2 = new aws.ec2.Instance(
`${name}-ec2`,
{
ami: Ec2AMI.AmazonLinux2023.ARM,
ami: AmazonLinux2023_ARM_EC2_AMI.id,
associatePublicIpAddress: false,
instanceType: 't4g.nano',
iamInstanceProfile: ssmProfile.name,
Expand Down
1 change: 0 additions & 1 deletion src/components/project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,6 @@ export class Project extends pulumi.ComponentResource {

private createStaticSiteService(options: StaticSiteService) {
const { serviceName, ...staticSiteOptions } = options;
if (!this.hostedZoneId) throw new MissingHostedZoneId(options.type);
const service = new StaticSite(
serviceName,
{
Expand Down
38 changes: 27 additions & 11 deletions src/components/static-site.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ export type StaticSiteArgs = {
* The domain which will be used to access the static site.
* The domain or subdomain must belong to the provided hostedZone.
*/
domain: pulumi.Input<string>;
domain?: pulumi.Input<string>;
/**
* The ID of the hosted zone.
*/
hostedZoneId: pulumi.Input<string>;
hostedZoneId?: pulumi.Input<string>;
/**
* A map of tags to assign to the resource.
*/
Expand All @@ -23,7 +23,7 @@ export type StaticSiteArgs = {

export class StaticSite extends pulumi.ComponentResource {
name: string;
certificate: AcmCertificate;
certificate?: AcmCertificate;
bucket: aws.s3.Bucket;
cloudfront: aws.cloudfront.Distribution;

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

this.name = name;
const { domain, hostedZoneId, tags } = args;
this.certificate = this.createTlsCertificate({ domain, hostedZoneId });
const hasCustomDomain = domain && hostedZoneId;
if (domain && !hostedZoneId) {
throw new Error(
'StaticSite:hostedZoneId must be provided when the domain is specified',
);
}
if (hasCustomDomain) {
this.certificate = this.createTlsCertificate({ domain, hostedZoneId });
}
this.bucket = this.createPublicBucket({ tags });
this.cloudfront = this.createCloudfrontDistribution({ domain, tags });
this.createDnsRecord({ domain, hostedZoneId });
if (hasCustomDomain) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this not grouped with creating Tls certificate in line 46?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It depends on Cloudfront distribution and therefore it must be called after CloudFront creation

this.createDnsRecord({ domain, hostedZoneId });
}

this.registerOutputs();
}

private createTlsCertificate({
domain,
hostedZoneId,
}: Pick<StaticSiteArgs, 'domain' | 'hostedZoneId'>) {
}: Pick<Required<StaticSiteArgs>, 'domain' | 'hostedZoneId'>) {
const certificate = new AcmCertificate(
`${domain}-acm-certificate`,
{
Expand Down Expand Up @@ -120,14 +130,20 @@ export class StaticSite extends pulumi.ComponentResource {
{
enabled: true,
defaultRootObject: 'index.html',
aliases: [domain],
...(domain && { aliases: [domain] }),
isIpv6Enabled: true,
waitForDeployment: true,
httpVersion: 'http2and3',
viewerCertificate: {
acmCertificateArn: this.certificate.certificate.arn,
sslSupportMethod: 'sni-only',
minimumProtocolVersion: 'TLSv1.2_2021',
...(this.certificate
? {
acmCertificateArn: this.certificate.certificate.arn,
sslSupportMethod: 'sni-only',
minimumProtocolVersion: 'TLSv1.2_2021',
}
: {
cloudfrontDefaultCertificate: true,
}),
},
origins: [
{
Expand Down Expand Up @@ -171,7 +187,7 @@ export class StaticSite extends pulumi.ComponentResource {
private createDnsRecord({
domain,
hostedZoneId,
}: Pick<StaticSiteArgs, 'domain' | 'hostedZoneId'>) {
}: Pick<Required<StaticSiteArgs>, 'domain' | 'hostedZoneId'>) {
const cdnAliasRecord = new aws.route53.Record(
`${this.name}-cdn-route53-record`,
{
Expand Down
6 changes: 0 additions & 6 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,6 @@ export const PredefinedSize = {
},
} as const;

export const Ec2AMI = {
AmazonLinux2023: {
ARM: 'ami-0b40baa8c6b882e6c',
},
};

export const commonTags = {
Env: pulumi.getStack(),
Project: pulumi.getProject(),
Expand Down