Skip to content

Commit 550aa9a

Browse files
committed
feat(cdk): add amplify, monitoring, signing methods
1 parent b3ef63e commit 550aa9a

File tree

7 files changed

+1065
-819
lines changed

7 files changed

+1065
-819
lines changed

packages/cdk/package.json

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,22 +34,26 @@
3434
"ts-node": "^10.9.1"
3535
},
3636
"dependencies": {
37+
"@aws-cdk/aws-amplify-alpha": "2.174.1-alpha.0",
3738
"@aws-cdk/aws-apigatewayv2-alpha": "2.114.1-alpha.0",
3839
"@aws-cdk/aws-apigatewayv2-integrations-alpha": "2.114.1-alpha.0",
39-
"@aws-cdk/aws-kinesisfirehose-alpha": "2.131.0-alpha.0",
40-
"@aws-cdk/aws-kinesisfirehose-destinations-alpha": "2.131.0-alpha.0",
41-
"@aws-cdk/cloud-assembly-schema": "^2.131.0",
42-
"@aws-cdk/cx-api": "^2.131.0",
43-
"@aws-sdk/client-secrets-manager": "^3.675.0",
44-
"@aws-sdk/client-sns": "^3.675.0",
45-
"@aws-sdk/client-ssm": "^3.675.0",
40+
"@aws-cdk/aws-kinesisfirehose-alpha": "2.174.1-alpha.0",
41+
"@aws-cdk/aws-kinesisfirehose-destinations-alpha": "2.174.1-alpha.0",
42+
"@aws-cdk/aws-redshift-alpha": "2.174.1-alpha.0",
43+
"@aws-cdk/cloud-assembly-schema": "^39.1.38",
44+
"@aws-cdk/cx-api": "^2.174.1",
45+
"@aws-sdk/client-secrets-manager": "^3.723.0",
46+
"@aws-sdk/client-sns": "^3.723.0",
47+
"@aws-sdk/client-ssm": "^3.723.0",
48+
"@aws-solutions-constructs/aws-cloudfront-s3": "^2.76.0",
4649
"@dot/env": "workspace:*",
4750
"@dot/log": "workspace:^",
4851
"@smithy/types": "^2.3.1",
4952
"@swc-node/register": "^1.6.7",
5053
"@swc/core": "^1.3.84",
5154
"aws-cdk-lib": "^2.162.1",
5255
"camelcase": "^6.3.0",
56+
"cdk-monitoring-constructs": "^9.1.3",
5357
"chalk": "^4.1.2",
5458
"constructs": "^10.3.0",
5559
"nanoid": "3.3.4",

packages/cdk/src/constructs/Stack.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ export class DotStack extends Stack {
2727
public readonly appName: string;
2828
public readonly env: DeployEnvironment;
2929
public readonly envPrefix: string;
30+
public readonly isProd: boolean;
3031
public readonly ssmPrefix: string;
3132

3233
constructor(scope: App, props: DotStackProps) {
@@ -41,6 +42,7 @@ export class DotStack extends Stack {
4142
this.appName = envPrefix + (props.appName || props.name);
4243
this.env = env;
4344
this.envPrefix = envPrefix;
45+
this.isProd = env === 'prod';
4446
this.node.setContext('appName', this.appName);
4547
this.node.setContext('env', this.env);
4648
this.ssmPrefix = `/${env}/${props.appName || props.name}`;
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import * as Amplify from '@aws-cdk/aws-amplify-alpha';
2+
import { RemovalPolicy } from 'aws-cdk-lib';
3+
import { Asset } from 'aws-cdk-lib/aws-s3-assets';
4+
5+
import { DotStack } from '../constructs/Stack';
6+
7+
interface AddAmplifyAppOptions {
8+
distPath: string;
9+
domainName?: string;
10+
environmentVariables?: { [key: string]: string };
11+
name: string;
12+
pwaRedirect?: boolean;
13+
scope: DotStack;
14+
subdomain?: string;
15+
}
16+
17+
interface AddAmplifyAppResult {
18+
app: Amplify.App;
19+
}
20+
21+
export const addAmplifyApp = (options: AddAmplifyAppOptions): AddAmplifyAppResult => {
22+
const { distPath, domainName, environmentVariables = {}, name, pwaRedirect, scope } = options;
23+
const subdomain = options.subdomain ?? scope.env;
24+
const baseName = DotStack.baseName(name, '-app');
25+
const appName = scope.resourceName(baseName);
26+
27+
const asset = new Asset(scope, `${appName}-asset`, { path: distPath });
28+
const app = new Amplify.App(scope, appName, {
29+
appName
30+
});
31+
const branch = app.addBranch(scope.env, { asset, environmentVariables });
32+
33+
app.applyRemovalPolicy(RemovalPolicy.DESTROY);
34+
35+
// Note: needed for PWA routing
36+
if (pwaRedirect) {
37+
app.addCustomRule({
38+
source: '/<*>',
39+
status: Amplify.RedirectStatus.NOT_FOUND_REWRITE,
40+
target: '/index.html'
41+
});
42+
}
43+
44+
if (domainName) {
45+
const domain = app.addDomain(domainName, {
46+
enableAutoSubdomain: false
47+
});
48+
domain.mapSubDomain(branch, subdomain);
49+
}
50+
51+
return { app };
52+
};

packages/cdk/src/methods/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
export * from './api';
2+
export * from './amplify';
23
export * from './app';
34
export * from './backup';
45
export * from './cloudfront';
@@ -8,12 +9,14 @@ export * from './fargate';
89
export * from './function';
910
export * from './kinesis';
1011
export * from './layer';
12+
export * from './monitoring';
1113
export * from './node-function';
1214
export * from './output';
1315
export * from './policy';
1416
export * from './queue';
1517
export * from './s3';
1618
export * from './secret';
19+
export * from './signing';
1720
export * from './sns';
1821
export * from './ssm';
1922
export * from './stack';
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import type { ApplicationLoadBalancedFargateService } from 'aws-cdk-lib/aws-ecs-patterns';
2+
import { MonitoringFacade, SnsAlarmActionStrategy } from 'cdk-monitoring-constructs';
3+
4+
import type { DotStack } from '../constructs/Stack';
5+
6+
import { addTopic } from './sns';
7+
8+
interface AddMonitoringOptions {
9+
emailAddress: string;
10+
fargateService: ApplicationLoadBalancedFargateService;
11+
scope: DotStack;
12+
}
13+
14+
export const addFargateMonitoring = (options: AddMonitoringOptions) => {
15+
const { emailAddress, fargateService, scope } = options;
16+
17+
const { topic: onAlarmTopic } = addTopic({ emailAddress, name: 'alarm', scope });
18+
19+
const monitoring = new MonitoringFacade(scope, scope.resourceName('monitor'), {
20+
alarmFactoryDefaults: {
21+
action: new SnsAlarmActionStrategy({ onAlarmTopic }),
22+
actionsEnabled: true,
23+
alarmNamePrefix: scope.resourceName('alarm')
24+
}
25+
});
26+
27+
monitoring.monitorFargateService({
28+
addCpuUsageAlarm: {
29+
Warning: {
30+
maxUsagePercent: 80
31+
}
32+
},
33+
addHealthyTaskPercentAlarm: {
34+
Warning: {
35+
minHealthyTaskPercent: 75
36+
}
37+
},
38+
addMemoryUsageAlarm: {
39+
Warning: {
40+
maxUsagePercent: 80
41+
}
42+
},
43+
addToAlarmDashboard: true,
44+
addToDetailDashboard: true,
45+
addToSummaryDashboard: true,
46+
fargateService
47+
});
48+
};
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import { generateKeyPairSync } from 'crypto';
2+
3+
import { PublicKey } from 'aws-cdk-lib/aws-cloudfront';
4+
5+
import { type DotStack } from '../constructs/Stack';
6+
7+
import { addSecret } from './secret';
8+
import { addParam } from './ssm';
9+
10+
const generateRsaKeyPair = () => {
11+
const { privateKey, publicKey } = generateKeyPairSync('rsa', {
12+
modulusLength: 2048,
13+
privateKeyEncoding: {
14+
format: 'pem',
15+
type: 'pkcs8'
16+
},
17+
publicKeyEncoding: {
18+
format: 'pem',
19+
type: 'spki'
20+
}
21+
});
22+
return { privateKey, publicKey };
23+
};
24+
25+
export const addSigningKey = (scope: DotStack) => {
26+
// FIXME: We have to not run this for additional deploys to prod
27+
// because for some reason it fails if the public key exists already
28+
// https://github.com/aws/aws-cdk/issues/15301
29+
const keyPair = generateRsaKeyPair();
30+
31+
addSecret({
32+
name: `${scope.env}-signing-key-pair`,
33+
scope,
34+
secretName: `${scope.ssmPrefix}/key/signing`,
35+
value: JSON.stringify(keyPair)
36+
});
37+
38+
const baseName = 'signing-pubkey';
39+
const publicKeyName = scope.resourceName(baseName);
40+
const cfKey = new PublicKey(scope, publicKeyName, {
41+
encodedKey: keyPair.publicKey,
42+
publicKeyName
43+
});
44+
45+
scope.overrideId(cfKey, publicKeyName);
46+
47+
addParam({
48+
id: `${publicKeyName}-id`,
49+
name: `${scope.ssmPrefix}/id/${baseName}`,
50+
scope,
51+
value: cfKey.publicKeyId
52+
});
53+
};

0 commit comments

Comments
 (0)