Skip to content

Commit 025f752

Browse files
author
Paulo Pereira
committed
feat(typescript): Added CloudFront Functions example
1 parent 7f2d778 commit 025f752

File tree

16 files changed

+395
-0
lines changed

16 files changed

+395
-0
lines changed
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
*.js
2+
!jest.config.js
3+
*.d.ts
4+
node_modules
5+
6+
# CDK asset staging directory
7+
.cdk.staging
8+
cdk.out
9+
package-lock.json
10+
.idea/
11+
**/.DS_Store
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
*.ts
2+
!*.d.ts
3+
4+
# CDK asset staging directory
5+
.cdk.staging
6+
cdk.out
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
# demo-cloudfront-functions
2+
3+
This project will create a S3 bucket with simple `html` files that will serve as our website source code, a
4+
CloudFront distribution to serve this content as our CDN and, two CloudFront functions that will work upon the request
5+
and response.
6+
7+
> This project is intended to be just a sample demonstration. Please, do not use it in production.
8+
9+
## CDK Toolkit
10+
11+
The `cdk.json` file tells the CDK Toolkit how to execute your app.
12+
13+
To start working with the project, first you will need to install all dependencies as well as the cdk module (if not
14+
installed already). In the project directory, run:
15+
16+
```bash
17+
$ npm install -g aws-cdk
18+
$ npm install
19+
```
20+
21+
## Deploying the solution
22+
23+
To deploy the solution, we will need to request cdk to deploy the stack:
24+
25+
```shell
26+
$ cdk deploy --all
27+
```
28+
29+
> **Note** that after running the deploy command, you will be presented and Output in the console like bellow:\
30+
> `DemoCloudfrontFunctionsStack.DistributionDomainName = xxxxxxxx.cloudfront.net`\
31+
> We will use this URL to access and test the website.
32+
33+
## Testing the solution
34+
35+
To begin the tests, you must have the distribution's URL (returned by cdk execution with the
36+
name `DistributionDomainName`), and web browser capable of analysing Network requests and responses (e.g. Google Chrome
37+
with Developer Tools enabled) or similar tool (e.g. curl, wget).
38+
39+
### Testing the index url
40+
41+
1. Access the base distribution URL
42+
2. A return code 200 is returned
43+
3. The response body will contain the text `It works!`
44+
45+
### Testing the index url with query strings
46+
47+
1. Access the base distribution URL, appending `?foo=bar` at its end
48+
2. A return code 200 is returned
49+
3. The response body will contain the text `It works!`
50+
51+
### Testing the test route
52+
53+
1. Access the base distribution URL, appending `/test` or `/test.html` at its end
54+
2. A return code 308 (permanent redirect) is returned
55+
3. The url will have changed to `/subdir/test.html`
56+
4. A return code 200 is returned
57+
5. The response body will contain the text `This is a test file for you`
58+
59+
### Testing the invalid route
60+
61+
1. Access the base distribution URL, appending `/invalid` at its end
62+
2. A return code 403 is returned
63+
3. There won't be any response body
64+
65+
### Other test cases
66+
67+
You can explore the `/cloudfront-functions/request-function.js` and `/cloudfront-functions/response-function.js` for
68+
more handling rules. Some of them are validated through _CloudWatch Logs_ and _X-Ray_ given their nature.
69+
70+
## Destroying the deployment
71+
72+
To destroy the provisioned infrastructure, you can simply run the following command:
73+
74+
```shell
75+
$ cdk destroy --all
76+
```
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#!/usr/bin/env node
2+
import 'source-map-support/register';
3+
import * as cdk from 'aws-cdk-lib';
4+
import {DemoCloudfrontFunctionsStack} from '../lib/demo-cloudfront-functions-stack';
5+
6+
const app = new cdk.App();
7+
8+
new DemoCloudfrontFunctionsStack(app, 'DemoCloudfrontFunctionsStack', {});
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
{
2+
"app": "npx ts-node --prefer-ts-exts bin/demo-cloudfront-functions.ts",
3+
"watch": {
4+
"include": [
5+
"**"
6+
],
7+
"exclude": [
8+
"README.md",
9+
"cdk*.json",
10+
"**/*.d.ts",
11+
"**/*.js",
12+
"tsconfig.json",
13+
"package*.json",
14+
"yarn.lock",
15+
"node_modules",
16+
"test"
17+
]
18+
},
19+
"context": {
20+
"@aws-cdk/aws-lambda:recognizeLayerVersion": true,
21+
"@aws-cdk/core:checkSecretUsage": true,
22+
"@aws-cdk/core:target-partitions": [
23+
"aws",
24+
"aws-cn"
25+
],
26+
"@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true,
27+
"@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true,
28+
"@aws-cdk/aws-ecs:arnFormatIncludesClusterName": true,
29+
"@aws-cdk/aws-iam:minimizePolicies": true,
30+
"@aws-cdk/core:validateSnapshotRemovalPolicy": true,
31+
"@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": true,
32+
"@aws-cdk/aws-s3:createDefaultLoggingPolicy": true,
33+
"@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption": true,
34+
"@aws-cdk/aws-apigateway:disableCloudWatchRole": true,
35+
"@aws-cdk/core:enablePartitionLiterals": true,
36+
"@aws-cdk/aws-events:eventsTargetQueueSameAccount": true,
37+
"@aws-cdk/aws-iam:standardizedServicePrincipals": true,
38+
"@aws-cdk/aws-ecs:disableExplicitDeploymentControllerForCircuitBreaker": true,
39+
"@aws-cdk/aws-iam:importedRoleStackSafeDefaultPolicyName": true,
40+
"@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy": true,
41+
"@aws-cdk/aws-route53-patters:useCertificate": true,
42+
"@aws-cdk/customresources:installLatestAwsSdkDefault": false,
43+
"@aws-cdk/aws-rds:databaseProxyUniqueResourceName": true,
44+
"@aws-cdk/aws-codedeploy:removeAlarmsFromDeploymentGroup": true,
45+
"@aws-cdk/aws-apigateway:authorizerChangeDeploymentLogicalId": true,
46+
"@aws-cdk/aws-ec2:launchTemplateDefaultUserData": true,
47+
"@aws-cdk/aws-secretsmanager:useAttachedSecretResourcePolicyForSecretTargetAttachments": true,
48+
"@aws-cdk/aws-redshift:columnId": true,
49+
"@aws-cdk/aws-stepfunctions-tasks:enableEmrServicePolicyV2": true,
50+
"@aws-cdk/aws-ec2:restrictDefaultSecurityGroup": true,
51+
"@aws-cdk/aws-apigateway:requestValidatorUniqueId": true,
52+
"@aws-cdk/aws-kms:aliasNameRef": true,
53+
"@aws-cdk/aws-autoscaling:generateLaunchTemplateInsteadOfLaunchConfig": true,
54+
"@aws-cdk/core:includePrefixInUniqueNameGeneration": true,
55+
"@aws-cdk/aws-efs:denyAnonymousAccess": true,
56+
"@aws-cdk/aws-opensearchservice:enableOpensearchMultiAzWithStandby": true,
57+
"@aws-cdk/aws-lambda-nodejs:useLatestRuntimeVersion": true,
58+
"@aws-cdk/aws-efs:mountTargetOrderInsensitiveLogicalId": true,
59+
"@aws-cdk/aws-rds:auroraClusterChangeScopeOfInstanceParameterGroupWithEachParameters": true,
60+
"@aws-cdk/aws-appsync:useArnForSourceApiAssociationIdentifier": true,
61+
"@aws-cdk/aws-rds:preventRenderingDeprecatedCredentials": true,
62+
"@aws-cdk/aws-codepipeline-actions:useNewDefaultBranchForCodeCommitSource": true
63+
}
64+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
{
2+
version: '1.0',
3+
context: {
4+
distributionDomainName: 'd2kfsug7s5ks8g.cloudfront.net',
5+
distributionId: 'E3LZ6RG30U90YL',
6+
eventType: 'viewer-request',
7+
requestId: 'Rhe19LLq_Jc8J07_mtt9h7sK-bMlvSdFPAu0TSjnQuTIgNQZ2qat9A=='
8+
},
9+
viewer: {
10+
ip: '89.180.57.11'
11+
},
12+
request: {
13+
method: 'GET',
14+
uri: '/',
15+
querystring: {},
16+
headers: {
17+
accept: {
18+
value: 'application/html'
19+
},
20+
x-correlation-id: {
21+
value: 'abcde'
22+
},
23+
host: {
24+
value: 'd2kfsug7s5ks8g.cloudfront.net'
25+
},
26+
user-agent: {
27+
value: 'Apache-HttpClient/4.5.14 (Java/17.0.9)'
28+
},
29+
accept-encoding: {
30+
value: 'br,deflate,gzip,x-gzip'
31+
}
32+
},
33+
cookies: {
34+
foo: {
35+
value: 'bar'
36+
}
37+
}
38+
}
39+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
GET http://d2kfsug7s5ks8g.cloudfront.net/
2+
Accept: application/html
3+
X-Correlation-ID: abcde
4+
Cookie: foo=bar
5+
6+
###
7+
GET http://d2kfsug7s5ks8g.cloudfront.net/ClixFeeder
8+
Accept: application/html
9+
10+
###
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
module.exports = {
2+
testEnvironment: 'node',
3+
roots: ['<rootDir>/test'],
4+
testMatch: ['**/*.test.ts'],
5+
transform: {
6+
'^.+\\.tsx?$': 'ts-jest'
7+
}
8+
};
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import * as cdk from 'aws-cdk-lib';
2+
import {RemovalPolicy} from 'aws-cdk-lib';
3+
import {Construct} from 'constructs';
4+
import {BlockPublicAccess, Bucket, BucketEncryption} from "aws-cdk-lib/aws-s3";
5+
import {BucketDeployment, Source} from "aws-cdk-lib/aws-s3-deployment";
6+
import {
7+
AllowedMethods,
8+
Distribution,
9+
Function,
10+
FunctionCode,
11+
FunctionEventType,
12+
FunctionRuntime,
13+
OriginAccessIdentity
14+
} from "aws-cdk-lib/aws-cloudfront";
15+
import {BehaviorOptions} from "aws-cdk-lib/aws-cloudfront/lib/distribution";
16+
import {S3Origin} from "aws-cdk-lib/aws-cloudfront-origins";
17+
18+
export class DemoCloudfrontFunctionsStack extends cdk.Stack {
19+
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
20+
super(scope, id, props);
21+
22+
// create a bucket to deploy the website files
23+
const bucket = new Bucket(this, 'WebsiteBucket', {
24+
encryption: BucketEncryption.S3_MANAGED,
25+
versioned: true,
26+
enforceSSL: true,
27+
blockPublicAccess: BlockPublicAccess.BLOCK_ALL,
28+
removalPolicy: RemovalPolicy.DESTROY
29+
});
30+
31+
// create a s3 bucket deployment to deploy the website directory's files to the website bucket
32+
new BucketDeployment(this, 'WebsiteFiles', {
33+
destinationBucket: bucket,
34+
sources: [Source.asset('./website')],
35+
contentType: 'text/html',
36+
retainOnDelete: false,
37+
});
38+
39+
// create an Origin Access Identity for CloudFront
40+
const cloudfrontOAI = new OriginAccessIdentity(this, 'cloudfront-OAI', {
41+
comment: `OAI for ${id}`
42+
});
43+
44+
// grant read permissions on the bucket to the CloudFront's Origin Access Identity
45+
bucket.grantRead(cloudfrontOAI);
46+
47+
// create a cloudFront function from the request-function.js file
48+
const requestFunction = new Function(this, 'RequestFunction', {
49+
functionName: 'RequestFunction',
50+
runtime: FunctionRuntime.JS_2_0,
51+
code: FunctionCode.fromFile({
52+
filePath: './functions/request-function.js'
53+
})
54+
});
55+
56+
// create a cloudFront function from the response-function.js file
57+
const responseFunction = new Function(this, 'ResponseFunction', {
58+
functionName: 'ResponseFunction',
59+
runtime: FunctionRuntime.JS_2_0,
60+
code: FunctionCode.fromFile({
61+
filePath: './functions/response-function.js'
62+
})
63+
});
64+
65+
// create a CloudFront behavior with origin of my website bucket and both request and response functions
66+
const defaultBehavior: BehaviorOptions = {
67+
origin: new S3Origin(bucket),
68+
compress: true,
69+
allowedMethods: AllowedMethods.ALLOW_ALL,
70+
functionAssociations: [
71+
{
72+
function: requestFunction,
73+
eventType: FunctionEventType.VIEWER_REQUEST,
74+
},
75+
{
76+
function: responseFunction,
77+
eventType: FunctionEventType.VIEWER_RESPONSE,
78+
}
79+
]
80+
};
81+
82+
// create a CloudFront distribution with the behavior created
83+
const distribution = new Distribution(this, 'SiteDistribution', {
84+
comment: 'CloudFront Functions example',
85+
defaultRootObject: 'index.html',
86+
defaultBehavior: defaultBehavior,
87+
});
88+
89+
// create an output with the CloudFront distribution URL
90+
new cdk.CfnOutput(this, 'DistributionDomainName', {
91+
value: distribution.domainName,
92+
});
93+
94+
}
95+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
{
2+
"name": "demo-cloudfront-functions",
3+
"version": "0.1.0",
4+
"bin": {
5+
"demo-cloudfront-functions": "bin/demo-cloudfront-functions.js"
6+
},
7+
"scripts": {
8+
"build": "tsc",
9+
"watch": "tsc -w",
10+
"test": "jest",
11+
"cdk": "cdk"
12+
},
13+
"devDependencies": {
14+
"@types/jest": "^29.5.11",
15+
"@types/node": "20.11.6",
16+
"jest": "^29.7.0",
17+
"ts-jest": "^29.1.2",
18+
"aws-cdk": "2.123.0",
19+
"ts-node": "^10.9.2",
20+
"typescript": "~5.3.3"
21+
},
22+
"dependencies": {
23+
"aws-cdk-lib": "2.123.0",
24+
"constructs": "^10.0.0",
25+
"source-map-support": "^0.5.21"
26+
}
27+
}

0 commit comments

Comments
 (0)