Skip to content

Commit 272eb31

Browse files
committed
feat(cdk): add CloudFront for HTTPS termination
Add CloudFront distribution for HTTPS access to LocalGov Drupal using the default CloudFront domain certificate (*.cloudfront.net). Architecture: Users -> CloudFront (HTTPS) -> ALB (HTTP) -> ECS Fargate - Create CloudFrontConstruct with disabled caching (Drupal handles it) - Update DrupalUrl output to show HTTPS URL as primary - Add DrupalUrlHttp for direct ALB access (debugging) - CloudFront free tier: 1TB/month, 10M requests/month
1 parent dd20371 commit 272eb31

2 files changed

Lines changed: 82 additions & 16 deletions

File tree

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import * as cdk from 'aws-cdk-lib';
2+
import * as cloudfront from 'aws-cdk-lib/aws-cloudfront';
3+
import * as origins from 'aws-cdk-lib/aws-cloudfront-origins';
4+
import * as elbv2 from 'aws-cdk-lib/aws-elasticloadbalancingv2';
5+
import { Construct } from 'constructs';
6+
7+
/**
8+
* Properties for CloudFront construct
9+
*/
10+
export interface CloudFrontConstructProps {
11+
/**
12+
* The Application Load Balancer to use as origin
13+
*/
14+
readonly loadBalancer: elbv2.IApplicationLoadBalancer;
15+
}
16+
17+
/**
18+
* CloudFront distribution for HTTPS termination.
19+
*
20+
* Provides HTTPS access to LocalGov Drupal using CloudFront's default
21+
* domain certificate (*.cloudfront.net). This is the simplest way to
22+
* add HTTPS without requiring a custom domain or ACM certificate.
23+
*
24+
* Architecture:
25+
* Users -> CloudFront (HTTPS) -> ALB (HTTP) -> ECS Fargate
26+
*
27+
* Cost: CloudFront free tier includes 1TB/month and 10M requests/month.
28+
* Demo usage stays well within free tier.
29+
*/
30+
export class CloudFrontConstruct extends Construct {
31+
/**
32+
* The CloudFront distribution
33+
*/
34+
public readonly distribution: cloudfront.Distribution;
35+
36+
/**
37+
* The CloudFront domain name (e.g., d123abc.cloudfront.net)
38+
*/
39+
public readonly domainName: string;
40+
41+
constructor(scope: Construct, id: string, props: CloudFrontConstructProps) {
42+
super(scope, id);
43+
44+
this.distribution = new cloudfront.Distribution(this, 'Distribution', {
45+
comment: 'LocalGov Drupal - HTTPS Termination',
46+
defaultBehavior: {
47+
origin: new origins.LoadBalancerV2Origin(props.loadBalancer, {
48+
protocolPolicy: cloudfront.OriginProtocolPolicy.HTTP_ONLY,
49+
}),
50+
viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
51+
allowedMethods: cloudfront.AllowedMethods.ALLOW_ALL,
52+
// Disable caching - Drupal handles its own caching
53+
cachePolicy: cloudfront.CachePolicy.CACHING_DISABLED,
54+
// Forward all headers, cookies, and query strings to origin
55+
originRequestPolicy: cloudfront.OriginRequestPolicy.ALL_VIEWER,
56+
},
57+
});
58+
59+
this.domainName = this.distribution.distributionDomainName;
60+
61+
// Output the HTTPS URL
62+
new cdk.CfnOutput(this, 'HttpsUrl', {
63+
value: `https://${this.domainName}`,
64+
description: 'HTTPS URL (CloudFront)',
65+
});
66+
}
67+
}

cloudformation/scenarios/localgov-drupal/cdk/lib/localgov-drupal-stack.ts

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import * as cdk from 'aws-cdk-lib';
22
import { Construct } from 'constructs';
3+
import { CloudFrontConstruct } from './constructs/cloudfront';
34
import { ComputeConstruct } from './constructs/compute';
45
import { DatabaseConstruct } from './constructs/database';
56
import { NetworkingConstruct } from './constructs/networking';
@@ -76,17 +77,8 @@ export class LocalGovDrupalStack extends cdk.Stack {
7677
deploymentMode,
7778
});
7879

79-
// Story 1.8 - WaitCondition for Drupal initialization
80-
// Creates a presigned URL that the container uses to signal completion
81-
const waitHandle = new cdk.CfnWaitConditionHandle(this, 'DrupalInitHandle');
82-
83-
const waitCondition = new cdk.CfnWaitCondition(this, 'DrupalInitCondition', {
84-
handle: waitHandle.ref,
85-
timeout: '900', // 15 minutes - allows for Aurora cold start + Drupal install
86-
count: 1,
87-
});
88-
8980
// Story 1.7 - Compute construct (Fargate, ALB)
81+
// Note: WaitCondition removed - container image doesn't signal completion
9082
const compute = new ComputeConstruct(this, 'Compute', {
9183
vpc: networking.vpc,
9284
albSecurityGroup: networking.albSecurityGroup,
@@ -96,23 +88,30 @@ export class LocalGovDrupalStack extends cdk.Stack {
9688
fileSystem: storage.fileSystem,
9789
accessPoint: storage.accessPoint,
9890
deploymentMode,
99-
waitConditionUrl: waitHandle.ref,
10091
});
10192

102-
// Ensure WaitCondition depends on the ECS service being created
103-
waitCondition.node.addDependency(compute.service);
93+
// CloudFront distribution for HTTPS termination
94+
const cdn = new CloudFrontConstruct(this, 'CDN', {
95+
loadBalancer: compute.loadBalancer,
96+
});
10497

10598
// ==========================================================================
10699
// Story 1.12 - CloudFormation Outputs
107100
// ==========================================================================
108101

109-
// Drupal URL - primary access point
102+
// Drupal URL - primary access point (HTTPS via CloudFront)
110103
new cdk.CfnOutput(this, 'DrupalUrl', {
111-
description: 'URL to access LocalGov Drupal',
112-
value: `http://${compute.loadBalancerDnsName}`,
104+
description: 'URL to access LocalGov Drupal (HTTPS)',
105+
value: `https://${cdn.domainName}`,
113106
exportName: `${this.stackName}-DrupalUrl`,
114107
});
115108

109+
// HTTP URL (ALB direct) - for debugging only
110+
new cdk.CfnOutput(this, 'DrupalUrlHttp', {
111+
description: 'Direct ALB URL (HTTP, for debugging)',
112+
value: `http://${compute.loadBalancerDnsName}`,
113+
});
114+
116115
// Admin credentials for first-time login
117116
new cdk.CfnOutput(this, 'AdminUsername', {
118117
description: 'Drupal admin username',

0 commit comments

Comments
 (0)