From fbd295440c06346885d60bac7517870a1640277c Mon Sep 17 00:00:00 2001 From: Marco Jahn Date: Fri, 2 May 2025 11:45:10 +0200 Subject: [PATCH 1/9] added pattern --- .../README.md | 130 +++++++++++++++++ ...udfront-keyvaluestore-apigw-routing-cdk.ts | 12 ++ ...dfront-keyvaluestore-apigw-routing-cdk.png | Bin 0 -> 47562 bytes .../example-pattern.json | 68 +++++++++ .../lib/pattern-stack.ts | 133 ++++++++++++++++++ .../package.json | 25 ++++ .../tsconfig.json | 31 ++++ 7 files changed, 399 insertions(+) create mode 100644 cloudfront-keyvaluestore-apigw-routing-cdk/README.md create mode 100644 cloudfront-keyvaluestore-apigw-routing-cdk/bin/cloudfront-keyvaluestore-apigw-routing-cdk.ts create mode 100644 cloudfront-keyvaluestore-apigw-routing-cdk/cloudfront-keyvaluestore-apigw-routing-cdk.png create mode 100644 cloudfront-keyvaluestore-apigw-routing-cdk/example-pattern.json create mode 100644 cloudfront-keyvaluestore-apigw-routing-cdk/lib/pattern-stack.ts create mode 100644 cloudfront-keyvaluestore-apigw-routing-cdk/package.json create mode 100644 cloudfront-keyvaluestore-apigw-routing-cdk/tsconfig.json diff --git a/cloudfront-keyvaluestore-apigw-routing-cdk/README.md b/cloudfront-keyvaluestore-apigw-routing-cdk/README.md new file mode 100644 index 000000000..9c1ba035c --- /dev/null +++ b/cloudfront-keyvaluestore-apigw-routing-cdk/README.md @@ -0,0 +1,130 @@ +# CloudFront with API Gateway Routing using Key Value Store + +This pattern demonstrates how to use Amazon CloudFront with CloudFront Functions to dynamically route traffic between multiple Amazon API Gateway endpoints. The routing decisions are based on values stored in CloudFront Key Value Store, allowing for flexible, configuration-driven request routing without redeploying your infrastructure. +This example uses an equal (50:50 distribution) between both API Gateways. For more informations on "cell partitioning" you can read the [AWS Well-Architected Guide - Cell Partition](https://docs.aws.amazon.com/wellarchitected/latest/reducing-scope-of-impact-with-cell-based-architecture/cell-partition.html) + +Learn more about this pattern at Serverless Land Patterns: [https://serverlessland.com/patterns/cloudfront-keyvaluestore-apigw-routing-cdk](https://serverlessland.com/patterns/cloudfront-keyvaluestore-apigw-routing-cdk) + + +Important: this application uses various AWS services and there are costs associated with these services after the Free Tier usage - please see the [AWS Pricing page](https://aws.amazon.com/pricing/) for details. You are responsible for any AWS costs incurred. No warranty is implied in this example. + +## Requirements + +* [Create an AWS account](https://portal.aws.amazon.com/gp/aws/developer/registration/index.html) if you do not already have one and log in. The IAM user that you use must have sufficient permissions to make necessary AWS service calls and manage AWS resources. +* [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured +* [Git Installed](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) +* [Node.js and npm](https://nodejs.org/) installed +* [AWS CDK](https://docs.aws.amazon.com/cdk/latest/guide/getting_started.html) installed + +## Deployment Instructions + +1. Create a new directory, navigate to that directory in a terminal and clone the GitHub repository: + + ``` + git clone https://github.com/aws-samples/serverless-patterns + ``` + +1. Change directory to the pattern directory: + + ``` + cd cloudfront-keyvaluestore-apigw-routing-cdk + ``` + +1. Install dependencies: + + ``` + npm install + ``` + +1. Deploy the CDK stack to your default AWS account and region: + + ``` + cdk deploy + ``` + **The deployment will take a couple of minutes** + +1. Note the outputs from the CDK deployment process. These contain the resource URLs and ARNs which are used for testing. + + +## How it works + +![Architecture Diagram](./cloudfront-keyvaluestore-apigw-routing-cdk.png) + +1. The client makes a request to the CloudFront distribution +1. The CloudFront Function executes on viewer request events +1. The function retrieves routing information from CloudFront Key Value Store +1. Based on the retrieved value, the function redirects the request to one of the two API Gateway endpoints +1. The selected API Gateway returns its response to the client + +In a real-world scenario: + +* The available targets would be maintained in the Key Value Store +* More complex routing logic would be implemented in the CloudFront function (e.g., routing based on user attributes, geographic location, A/B testing scenarios) + +## Testing + +1. Get Key Value Store `ETAG` and note it down. + + ``` + aws cloudfront-keyvaluestore describe-key-value-store \ + --kvs-arn=[KVSTOREARN] + ``` + +1. Add entries to the Key Value Storereplace. Replace `[KVSTOREARN]`, `[APIGATEWAY1URL]`, and `[APIGATEWAY2URL]` with the noted outputs. + + ``` + aws cloudfront-keyvaluestore update-keys \ + --kvs-arn[KVSTOREARN] \ + --if-match=[ETAG] \ + --puts '[ + { + "Key": "APIGW1URL", + "Value": "[APIGATEWAY1URL] " + }, + { + "Key": "APIGW2URL", + "Value": "[APIGATEWAY2URL]" + } + ]' + ``` + + If you receive something similar to, it worked + ```json + { + "ETag": "KV3UN6WX5RRO2AG", + "ItemCount": 2, + "TotalSizeInBytes": 145 + } + ``` + +1. Access the CloudFront URL in a browser or via curl, replace `[CLOUDFRONTDOMAINNAME]`: + + ``` + curl -i -L [CLOUDFRONTDOMAINNAME] + ``` + +1. The request should be redirected to either API Gateway 1 or API Gateway 2, and you should see a response like: + + ``` + {"message": "Hello from API 1"} + ``` + + or + + ``` + {"message": "Hello from API 2"} + ``` + +1. Make multiple requests to observe the random routing between the two API Gateway endpoints. + + +## Cleanup + +1. Delete the stack + ```bash + cdk destroy + ``` +---- +Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved. + +SPDX-License-Identifier: MIT-0 diff --git a/cloudfront-keyvaluestore-apigw-routing-cdk/bin/cloudfront-keyvaluestore-apigw-routing-cdk.ts b/cloudfront-keyvaluestore-apigw-routing-cdk/bin/cloudfront-keyvaluestore-apigw-routing-cdk.ts new file mode 100644 index 000000000..ef7947583 --- /dev/null +++ b/cloudfront-keyvaluestore-apigw-routing-cdk/bin/cloudfront-keyvaluestore-apigw-routing-cdk.ts @@ -0,0 +1,12 @@ +#!/usr/bin/env node +import * as cdk from "aws-cdk-lib"; +import { PatternStack } from "../lib/pattern-stack"; + +const app = new cdk.App(); +// amazonq-ignore-next-line +new PatternStack(app, "PatternStack", { + env: { + account: process.env.CDK_DEFAULT_ACCOUNT, + region: process.env.CDK_DEFAULT_REGION, + }, +}); diff --git a/cloudfront-keyvaluestore-apigw-routing-cdk/cloudfront-keyvaluestore-apigw-routing-cdk.png b/cloudfront-keyvaluestore-apigw-routing-cdk/cloudfront-keyvaluestore-apigw-routing-cdk.png new file mode 100644 index 0000000000000000000000000000000000000000..33e57442fa984fea18797ae892010f6527f45183 GIT binary patch literal 47562 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 literal 0 HcmV?d00001 diff --git a/cloudfront-keyvaluestore-apigw-routing-cdk/example-pattern.json b/cloudfront-keyvaluestore-apigw-routing-cdk/example-pattern.json new file mode 100644 index 000000000..84a847236 --- /dev/null +++ b/cloudfront-keyvaluestore-apigw-routing-cdk/example-pattern.json @@ -0,0 +1,68 @@ +{ + "title": "CloudFront with API Gateway Routing using Key Value Store", + "description": "Route traffic dynamically between API Gateway endpoints using CloudFront Functions and Key Value Store without redeploying infrastructure.", + "language": "TypeScript", + "level": "300", + "framework": "CDK", + "introBox": { + "headline": "How it works", + "text": [ + "This pattern demonstrates how to use Amazon CloudFront with CloudFront Functions to dynamically route traffic between multiple Amazon API Gateway endpoints.", + "The routing decisions are based on values stored in CloudFront Key Value Store, allowing for flexible, configuration-driven request routing without redeploying your infrastructure.", + "This example uses an equal (50:50 distribution) between both API Gateways, showcasing how to implement cell partitioning for your applications.", + "The pattern deploys a CloudFront distribution, CloudFront Function, CloudFront Key Value Store, and two API Gateway endpoints." + ] + }, + "gitHub": { + "template": { + "repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/cloudfront-keyvaluestore-apigw-routing-cdk", + "templateURL": "serverless-patterns/cloudfront-keyvaluestore-apigw-routing-cdk", + "projectFolder": "cloudfront-keyvaluestore-apigw-routing-cdk", + "templateFile": "lib/pattern-stack.ts" + } + }, + "resources": { + "bullets": [ + { + "text": "AWS Well-Architected Guide - Cell Partition", + "link": "https://docs.aws.amazon.com/wellarchitected/latest/reducing-scope-of-impact-with-cell-based-architecture/cell-partition.html" + }, + { + "text": "CloudFront Key Value Store Documentation", + "link": "https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/kvs-with-functions.html" + }, + { + "text": "CloudFront Functions Documentation", + "link": "https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/cloudfront-functions.html" + } + ] + }, + "deploy": { + "text": [ + "Clone the repository: git clone https://github.com/aws-samples/serverless-patterns", + "Change directory: cd cloudfront-keyvaluestore-apigw-routing-cdk", + "Install dependencies: npm install", + "Deploy the CDK stack: cdk deploy" + ] + }, + "testing": { + "text": [ + "1. Get Key Value Store ETAG: aws cloudfront-keyvaluestore describe-key-value-store --kvs-arn=[KVSTOREARN]", + "2. Add entries to the Key Value Store: aws cloudfront-keyvaluestore update-keys --kvs-arn=[KVSTOREARN] --if-match=[ETAG] --puts '[{\"Key\": \"APIGW1URL\", \"Value\": \"[APIGATEWAY1URL]\"},{\"Key\": \"APIGW2URL\", \"Value\": \"[APIGATEWAY2URL]\"}]'", + "3. Access CloudFront URL: curl -i -L [CLOUDFRONTDOMAINNAME]", + "4. The request should be redirected to either API Gateway 1 or API Gateway 2, showing a response like {\"message\": \"Hello from API 1\"} or {\"message\": \"Hello from API 2\"}", + "5. Make multiple requests to observe routing between the two API Gateway endpoints." + ] + }, + "cleanup": { + "text": ["Delete the stack: cdk destroy"] + }, + "authors": [ + { + "name": "Marco Jahn", + "image": "https://sessionize.com/image/e99b-400o400o2-pqR4BacUSzHrq4fgZ4wwEQ.png", + "bio": "Senior Solutions Architect, Amazon Web Services", + "linkedin": "marcojahn" + } + ] +} diff --git a/cloudfront-keyvaluestore-apigw-routing-cdk/lib/pattern-stack.ts b/cloudfront-keyvaluestore-apigw-routing-cdk/lib/pattern-stack.ts new file mode 100644 index 000000000..84e210c24 --- /dev/null +++ b/cloudfront-keyvaluestore-apigw-routing-cdk/lib/pattern-stack.ts @@ -0,0 +1,133 @@ +import * as cdk from "aws-cdk-lib"; +import * as cloudfront from "aws-cdk-lib/aws-cloudfront"; +import * as origins from "aws-cdk-lib/aws-cloudfront-origins"; +import * as apigateway from "aws-cdk-lib/aws-apigateway"; +import * as s3 from "aws-cdk-lib/aws-s3"; +import { Construct } from "constructs"; + +export class PatternStack extends cdk.Stack { + constructor(scope: Construct, id: string, props?: cdk.StackProps) { + super(scope, id, props); + + // create two Rest API GWs, both with a http status 200 mock response, but with individual payload + const api1 = new apigateway.RestApi(this, "api1", { + description: "API 1", + endpointConfiguration: { + types: [apigateway.EndpointType.REGIONAL], + }, + }); + api1.root.addMethod( + "GET", + new apigateway.MockIntegration({ + integrationResponses: [ + { + statusCode: "200", + responseTemplates: { + "application/json": `{"message": "Hello from API 1"}`, + }, + }, + ], + requestTemplates: { + "application/json": '{"statusCode": 200}', + }, + }), + { + methodResponses: [{ statusCode: "200" }], + }, + ); + const api2 = new apigateway.RestApi(this, "api2", { + description: "API 2", + endpointConfiguration: { + types: [apigateway.EndpointType.REGIONAL], + }, + }); + api2.root.addMethod( + "GET", + new apigateway.MockIntegration({ + integrationResponses: [ + { + statusCode: "200", + responseTemplates: { + "application/json": `{"message": "Hello from API 2"}`, + }, + }, + ], + requestTemplates: { + "application/json": '{"statusCode": 200}', + }, + }), + { + methodResponses: [{ statusCode: "200" }], + }, + ); + + const kvStore = new cloudfront.KeyValueStore(this, "KVStore", {}); + + const bucket = new s3.Bucket(this, "DistributionBucket", { + bucketName: `distribution-bucket-${this.account}`, + enforceSSL: true, + removalPolicy: cdk.RemovalPolicy.DESTROY, + }); + + // 2. Create CloudFront Function for redirects + const redirectFunction = new cloudfront.Function(this, "RedirectFunction", { + code: cloudfront.FunctionCode.fromInline(` + import cf from 'cloudfront'; + + // This fails if there is no key value store associated with the function + const kvsHandle = cf.kvs(); + + async function handler(event) { + const request = event.request; + + const kvKey = 'APIGW' + (Math.random() < 0.5 ? 1 : 2) + 'URL'; + + const redirectUrl = await kvsHandle.get(kvKey); + + const response = { + statusCode: 302, + statusDescription: 'Found', + headers: + { "location": { "value": redirectUrl } } + } + + return response; + } + `), + // Note that JS_2_0 must be used for Key Value Store support + runtime: cloudfront.FunctionRuntime.JS_2_0, + keyValueStore: kvStore, + }); + + // add cloudfront distribution with no behaviour + const cloudFrontDistribution = new cloudfront.Distribution(this, "CloudFrontDistribution", { + defaultBehavior: { + origin: origins.S3BucketOrigin.withOriginAccessControl(bucket), + functionAssociations: [ + { + function: redirectFunction, + eventType: cloudfront.FunctionEventType.VIEWER_REQUEST, + }, + ], + }, + }); + + // Output Cloudfront URL as CloudFormation output + new cdk.CfnOutput(this, "CLOUDFRONTDOMAINNAME", { + value: cloudFrontDistribution.distributionDomainName, + }); + + new cdk.CfnOutput(this, "APIGATEWAY1URL", { + value: api1.url, + }); + + new cdk.CfnOutput(this, "APIGATEWAY2URL", { + value: api2.url, + }); + + // output kv arn + new cdk.CfnOutput(this, "KVSTOREARN", { + value: kvStore.keyValueStoreArn, + }); + } +} diff --git a/cloudfront-keyvaluestore-apigw-routing-cdk/package.json b/cloudfront-keyvaluestore-apigw-routing-cdk/package.json new file mode 100644 index 000000000..1668398ec --- /dev/null +++ b/cloudfront-keyvaluestore-apigw-routing-cdk/package.json @@ -0,0 +1,25 @@ +{ + "name": "cloudfront-keyvaluestore-apigw-routing-cdk", + "version": "0.1.0", + "bin": { + "cloudfront-keyvaluestore-apigw-routing-cdk": "bin/cloudfront-keyvaluestore-apigw-routing-cdk.js" + }, + "scripts": { + "build": "tsc", + "watch": "tsc -w", + "cdk": "cdk" + }, + "devDependencies": { + "@types/jest": "^29.5.14", + "@types/node": "22.7.9", + "jest": "^29.7.0", + "ts-jest": "^29.2.5", + "aws-cdk": "2.1003.0", + "ts-node": "^10.9.2", + "typescript": "~5.6.3" + }, + "dependencies": { + "aws-cdk-lib": "2.181.1", + "constructs": "^10.0.0" + } +} diff --git a/cloudfront-keyvaluestore-apigw-routing-cdk/tsconfig.json b/cloudfront-keyvaluestore-apigw-routing-cdk/tsconfig.json new file mode 100644 index 000000000..aaa7dc510 --- /dev/null +++ b/cloudfront-keyvaluestore-apigw-routing-cdk/tsconfig.json @@ -0,0 +1,31 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "commonjs", + "lib": [ + "es2020", + "dom" + ], + "declaration": true, + "strict": true, + "noImplicitAny": true, + "strictNullChecks": true, + "noImplicitThis": true, + "alwaysStrict": true, + "noUnusedLocals": false, + "noUnusedParameters": false, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": false, + "inlineSourceMap": true, + "inlineSources": true, + "experimentalDecorators": true, + "strictPropertyInitialization": false, + "typeRoots": [ + "./node_modules/@types" + ] + }, + "exclude": [ + "node_modules", + "cdk.out" + ] +} From a6d9634fd3f91c02fdd077d0a60c5e4772fd824d Mon Sep 17 00:00:00 2001 From: Marco Jahn Date: Fri, 2 May 2025 11:52:32 +0200 Subject: [PATCH 2/9] fixed service names --- cloudfront-keyvaluestore-apigw-routing-cdk/README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cloudfront-keyvaluestore-apigw-routing-cdk/README.md b/cloudfront-keyvaluestore-apigw-routing-cdk/README.md index 9c1ba035c..e4bef4c7d 100644 --- a/cloudfront-keyvaluestore-apigw-routing-cdk/README.md +++ b/cloudfront-keyvaluestore-apigw-routing-cdk/README.md @@ -1,11 +1,10 @@ -# CloudFront with API Gateway Routing using Key Value Store +# Amazon CloudFront with Amazon API Gateway Routing using Key Value Store This pattern demonstrates how to use Amazon CloudFront with CloudFront Functions to dynamically route traffic between multiple Amazon API Gateway endpoints. The routing decisions are based on values stored in CloudFront Key Value Store, allowing for flexible, configuration-driven request routing without redeploying your infrastructure. This example uses an equal (50:50 distribution) between both API Gateways. For more informations on "cell partitioning" you can read the [AWS Well-Architected Guide - Cell Partition](https://docs.aws.amazon.com/wellarchitected/latest/reducing-scope-of-impact-with-cell-based-architecture/cell-partition.html) Learn more about this pattern at Serverless Land Patterns: [https://serverlessland.com/patterns/cloudfront-keyvaluestore-apigw-routing-cdk](https://serverlessland.com/patterns/cloudfront-keyvaluestore-apigw-routing-cdk) - Important: this application uses various AWS services and there are costs associated with these services after the Free Tier usage - please see the [AWS Pricing page](https://aws.amazon.com/pricing/) for details. You are responsible for any AWS costs incurred. No warranty is implied in this example. ## Requirements From dac78b400bc8411f71b4cba4979d3f5e9199537d Mon Sep 17 00:00:00 2001 From: Marco Jahn Date: Mon, 5 May 2025 15:56:02 +0200 Subject: [PATCH 3/9] fixed api gateway service icon color --- ...dfront-keyvaluestore-apigw-routing-cdk.png | Bin 47562 -> 47518 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/cloudfront-keyvaluestore-apigw-routing-cdk/cloudfront-keyvaluestore-apigw-routing-cdk.png b/cloudfront-keyvaluestore-apigw-routing-cdk/cloudfront-keyvaluestore-apigw-routing-cdk.png index 33e57442fa984fea18797ae892010f6527f45183..e4504860c4973bc5d8bf0294435aa3385ce82f26 100644 GIT binary patch literal 47518 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 From 1566328b78cbf1b97d24bac02a7d75b97aa77456 Mon Sep 17 00:00:00 2001 From: Marco Date: Mon, 5 May 2025 15:57:23 +0200 Subject: [PATCH 4/9] Update cloudfront-keyvaluestore-apigw-routing-cdk/README.md Co-authored-by: Ben <9841563+bfreiberg@users.noreply.github.com> --- cloudfront-keyvaluestore-apigw-routing-cdk/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cloudfront-keyvaluestore-apigw-routing-cdk/README.md b/cloudfront-keyvaluestore-apigw-routing-cdk/README.md index e4bef4c7d..c85707da2 100644 --- a/cloudfront-keyvaluestore-apigw-routing-cdk/README.md +++ b/cloudfront-keyvaluestore-apigw-routing-cdk/README.md @@ -1,7 +1,7 @@ # Amazon CloudFront with Amazon API Gateway Routing using Key Value Store This pattern demonstrates how to use Amazon CloudFront with CloudFront Functions to dynamically route traffic between multiple Amazon API Gateway endpoints. The routing decisions are based on values stored in CloudFront Key Value Store, allowing for flexible, configuration-driven request routing without redeploying your infrastructure. -This example uses an equal (50:50 distribution) between both API Gateways. For more informations on "cell partitioning" you can read the [AWS Well-Architected Guide - Cell Partition](https://docs.aws.amazon.com/wellarchitected/latest/reducing-scope-of-impact-with-cell-based-architecture/cell-partition.html) +This example uses an equal (50:50 distribution) between both API Gateways. For more informations on "cell partitioning" refer to the [AWS Well-Architected Guide - Cell Partition](https://docs.aws.amazon.com/wellarchitected/latest/reducing-scope-of-impact-with-cell-based-architecture/cell-partition.html) Learn more about this pattern at Serverless Land Patterns: [https://serverlessland.com/patterns/cloudfront-keyvaluestore-apigw-routing-cdk](https://serverlessland.com/patterns/cloudfront-keyvaluestore-apigw-routing-cdk) From f7e984732d8baed1818e9b6d765380c7395a7814 Mon Sep 17 00:00:00 2001 From: Marco Date: Mon, 5 May 2025 16:00:49 +0200 Subject: [PATCH 5/9] Update cloudfront-keyvaluestore-apigw-routing-cdk/example-pattern.json Co-authored-by: Ben <9841563+bfreiberg@users.noreply.github.com> --- cloudfront-keyvaluestore-apigw-routing-cdk/example-pattern.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cloudfront-keyvaluestore-apigw-routing-cdk/example-pattern.json b/cloudfront-keyvaluestore-apigw-routing-cdk/example-pattern.json index 84a847236..2b6a7a3ae 100644 --- a/cloudfront-keyvaluestore-apigw-routing-cdk/example-pattern.json +++ b/cloudfront-keyvaluestore-apigw-routing-cdk/example-pattern.json @@ -3,7 +3,7 @@ "description": "Route traffic dynamically between API Gateway endpoints using CloudFront Functions and Key Value Store without redeploying infrastructure.", "language": "TypeScript", "level": "300", - "framework": "CDK", + "framework": "AWS CDK", "introBox": { "headline": "How it works", "text": [ From bdad466483a33e2383568b29061bf6de8c1a0fe2 Mon Sep 17 00:00:00 2001 From: Marco Date: Mon, 5 May 2025 16:00:55 +0200 Subject: [PATCH 6/9] Update cloudfront-keyvaluestore-apigw-routing-cdk/example-pattern.json Co-authored-by: Ben <9841563+bfreiberg@users.noreply.github.com> --- cloudfront-keyvaluestore-apigw-routing-cdk/example-pattern.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cloudfront-keyvaluestore-apigw-routing-cdk/example-pattern.json b/cloudfront-keyvaluestore-apigw-routing-cdk/example-pattern.json index 2b6a7a3ae..4d49ca4ef 100644 --- a/cloudfront-keyvaluestore-apigw-routing-cdk/example-pattern.json +++ b/cloudfront-keyvaluestore-apigw-routing-cdk/example-pattern.json @@ -1,5 +1,5 @@ { - "title": "CloudFront with API Gateway Routing using Key Value Store", + "title": "Amazon CloudFront with API Gateway Routing using Key Value Store", "description": "Route traffic dynamically between API Gateway endpoints using CloudFront Functions and Key Value Store without redeploying infrastructure.", "language": "TypeScript", "level": "300", From bad6024da8e9918b8b85d6780f77e1ece950d1ad Mon Sep 17 00:00:00 2001 From: Marco Jahn Date: Mon, 5 May 2025 16:05:11 +0200 Subject: [PATCH 7/9] removed not needed dependencies, updated README.md --- cloudfront-keyvaluestore-apigw-routing-cdk/README.md | 5 +---- cloudfront-keyvaluestore-apigw-routing-cdk/package.json | 2 -- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/cloudfront-keyvaluestore-apigw-routing-cdk/README.md b/cloudfront-keyvaluestore-apigw-routing-cdk/README.md index c85707da2..82d7d23e9 100644 --- a/cloudfront-keyvaluestore-apigw-routing-cdk/README.md +++ b/cloudfront-keyvaluestore-apigw-routing-cdk/README.md @@ -55,10 +55,7 @@ Important: this application uses various AWS services and there are costs associ 1. Based on the retrieved value, the function redirects the request to one of the two API Gateway endpoints 1. The selected API Gateway returns its response to the client -In a real-world scenario: - -* The available targets would be maintained in the Key Value Store -* More complex routing logic would be implemented in the CloudFront function (e.g., routing based on user attributes, geographic location, A/B testing scenarios) +In a real-world scenario the available targets would be maintained in the Key Value Store depending on the desired business logic and requirements. ## Testing diff --git a/cloudfront-keyvaluestore-apigw-routing-cdk/package.json b/cloudfront-keyvaluestore-apigw-routing-cdk/package.json index 1668398ec..421c1ab7a 100644 --- a/cloudfront-keyvaluestore-apigw-routing-cdk/package.json +++ b/cloudfront-keyvaluestore-apigw-routing-cdk/package.json @@ -12,8 +12,6 @@ "devDependencies": { "@types/jest": "^29.5.14", "@types/node": "22.7.9", - "jest": "^29.7.0", - "ts-jest": "^29.2.5", "aws-cdk": "2.1003.0", "ts-node": "^10.9.2", "typescript": "~5.6.3" From bc45863257e315a9dbee898fcfde1afd29e5a273 Mon Sep 17 00:00:00 2001 From: Marco Jahn Date: Mon, 5 May 2025 16:20:14 +0200 Subject: [PATCH 8/9] added missing cdk.json --- .../cdk.json | 83 +++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 cloudfront-keyvaluestore-apigw-routing-cdk/cdk.json diff --git a/cloudfront-keyvaluestore-apigw-routing-cdk/cdk.json b/cloudfront-keyvaluestore-apigw-routing-cdk/cdk.json new file mode 100644 index 000000000..f8740512e --- /dev/null +++ b/cloudfront-keyvaluestore-apigw-routing-cdk/cdk.json @@ -0,0 +1,83 @@ +{ + "app": "npx ts-node --prefer-ts-exts bin/cloudfront-keyvaluestore-apigw-routing-cdk.ts", + "watch": { + "include": ["**"], + "exclude": [ + "README.md", + "cdk*.json", + "**/*.d.ts", + "**/*.js", + "tsconfig.json", + "package*.json", + "yarn.lock", + "node_modules", + "test" + ] + }, + "context": { + "@aws-cdk/aws-lambda:recognizeLayerVersion": true, + "@aws-cdk/core:checkSecretUsage": true, + "@aws-cdk/core:target-partitions": ["aws", "aws-cn"], + "@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true, + "@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true, + "@aws-cdk/aws-ecs:arnFormatIncludesClusterName": true, + "@aws-cdk/aws-iam:minimizePolicies": true, + "@aws-cdk/core:validateSnapshotRemovalPolicy": true, + "@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": true, + "@aws-cdk/aws-s3:createDefaultLoggingPolicy": true, + "@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption": true, + "@aws-cdk/aws-apigateway:disableCloudWatchRole": true, + "@aws-cdk/core:enablePartitionLiterals": true, + "@aws-cdk/aws-events:eventsTargetQueueSameAccount": true, + "@aws-cdk/aws-ecs:disableExplicitDeploymentControllerForCircuitBreaker": true, + "@aws-cdk/aws-iam:importedRoleStackSafeDefaultPolicyName": true, + "@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy": true, + "@aws-cdk/aws-route53-patters:useCertificate": true, + "@aws-cdk/customresources:installLatestAwsSdkDefault": false, + "@aws-cdk/aws-rds:databaseProxyUniqueResourceName": true, + "@aws-cdk/aws-codedeploy:removeAlarmsFromDeploymentGroup": true, + "@aws-cdk/aws-apigateway:authorizerChangeDeploymentLogicalId": true, + "@aws-cdk/aws-ec2:launchTemplateDefaultUserData": true, + "@aws-cdk/aws-secretsmanager:useAttachedSecretResourcePolicyForSecretTargetAttachments": true, + "@aws-cdk/aws-redshift:columnId": true, + "@aws-cdk/aws-stepfunctions-tasks:enableEmrServicePolicyV2": true, + "@aws-cdk/aws-ec2:restrictDefaultSecurityGroup": true, + "@aws-cdk/aws-apigateway:requestValidatorUniqueId": true, + "@aws-cdk/aws-kms:aliasNameRef": true, + "@aws-cdk/aws-autoscaling:generateLaunchTemplateInsteadOfLaunchConfig": true, + "@aws-cdk/core:includePrefixInUniqueNameGeneration": true, + "@aws-cdk/aws-efs:denyAnonymousAccess": true, + "@aws-cdk/aws-opensearchservice:enableOpensearchMultiAzWithStandby": true, + "@aws-cdk/aws-lambda-nodejs:useLatestRuntimeVersion": true, + "@aws-cdk/aws-efs:mountTargetOrderInsensitiveLogicalId": true, + "@aws-cdk/aws-rds:auroraClusterChangeScopeOfInstanceParameterGroupWithEachParameters": true, + "@aws-cdk/aws-appsync:useArnForSourceApiAssociationIdentifier": true, + "@aws-cdk/aws-rds:preventRenderingDeprecatedCredentials": true, + "@aws-cdk/aws-codepipeline-actions:useNewDefaultBranchForCodeCommitSource": true, + "@aws-cdk/aws-cloudwatch-actions:changeLambdaPermissionLogicalIdForLambdaAction": true, + "@aws-cdk/aws-codepipeline:crossAccountKeysDefaultValueToFalse": true, + "@aws-cdk/aws-codepipeline:defaultPipelineTypeToV2": true, + "@aws-cdk/aws-kms:reduceCrossAccountRegionPolicyScope": true, + "@aws-cdk/aws-eks:nodegroupNameAttribute": true, + "@aws-cdk/aws-ec2:ebsDefaultGp3Volume": true, + "@aws-cdk/aws-ecs:removeDefaultDeploymentAlarm": true, + "@aws-cdk/custom-resources:logApiResponseDataPropertyTrueDefault": false, + "@aws-cdk/aws-s3:keepNotificationInImportedBucket": false, + "@aws-cdk/aws-ecs:enableImdsBlockingDeprecatedFeature": false, + "@aws-cdk/aws-ecs:disableEcsImdsBlocking": true, + "@aws-cdk/aws-ecs:reduceEc2FargateCloudWatchPermissions": true, + "@aws-cdk/aws-dynamodb:resourcePolicyPerReplica": true, + "@aws-cdk/aws-ec2:ec2SumTImeoutEnabled": true, + "@aws-cdk/aws-appsync:appSyncGraphQLAPIScopeLambdaPermission": true, + "@aws-cdk/aws-rds:setCorrectValueForDatabaseInstanceReadReplicaInstanceResourceId": true, + "@aws-cdk/core:cfnIncludeRejectComplexResourceUpdateCreatePolicyIntrinsics": true, + "@aws-cdk/aws-lambda-nodejs:sdkV3ExcludeSmithyPackages": true, + "@aws-cdk/aws-stepfunctions-tasks:fixRunEcsTaskPolicy": true, + "@aws-cdk/aws-ec2:bastionHostUseAmazonLinux2023ByDefault": true, + "@aws-cdk/aws-route53-targets:userPoolDomainNameMethodWithoutCustomResource": true, + "@aws-cdk/aws-elasticloadbalancingV2:albDualstackWithoutPublicIpv4SecurityGroupRulesDefault": true, + "@aws-cdk/aws-iam:oidcRejectUnauthorizedConnections": true, + "@aws-cdk/core:enableAdditionalMetadataCollection": true, + "@aws-cdk/aws-lambda:createNewPoliciesWithAddToRolePolicy": true + } +} From f8cc299e7e0b56a6353fa166295b9beb53a97aa9 Mon Sep 17 00:00:00 2001 From: Ben <9841563+bfreiberg@users.noreply.github.com> Date: Mon, 5 May 2025 16:38:46 +0200 Subject: [PATCH 9/9] Add final pattern file --- ...front-keyvaluestore-apigw-routing-cdk.json | 111 ++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 cloudfront-keyvaluestore-apigw-routing-cdk/cloudfront-keyvaluestore-apigw-routing-cdk.json diff --git a/cloudfront-keyvaluestore-apigw-routing-cdk/cloudfront-keyvaluestore-apigw-routing-cdk.json b/cloudfront-keyvaluestore-apigw-routing-cdk/cloudfront-keyvaluestore-apigw-routing-cdk.json new file mode 100644 index 000000000..ddf7730f5 --- /dev/null +++ b/cloudfront-keyvaluestore-apigw-routing-cdk/cloudfront-keyvaluestore-apigw-routing-cdk.json @@ -0,0 +1,111 @@ +{ + "title": "Amazon CloudFront with API Gateway Routing using Key Value Store", + "description": "Route traffic dynamically between API Gateway endpoints using CloudFront Functions and Key Value Store without redeploying infrastructure.", + "language": "TypeScript", + "level": "300", + "framework": "AWS CDK", + "introBox": { + "headline": "How it works", + "text": [ + "This pattern demonstrates how to use Amazon CloudFront with CloudFront Functions to dynamically route traffic between multiple Amazon API Gateway endpoints.", + "The routing decisions are based on values stored in CloudFront Key Value Store, allowing for flexible, configuration-driven request routing without redeploying your infrastructure.", + "This example uses an equal (50:50 distribution) between both API Gateways, showcasing how to implement cell partitioning for your applications.", + "The pattern deploys a CloudFront distribution, CloudFront Function, CloudFront Key Value Store, and two API Gateway endpoints." + ] + }, + "gitHub": { + "template": { + "repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/cloudfront-keyvaluestore-apigw-routing-cdk", + "templateURL": "serverless-patterns/cloudfront-keyvaluestore-apigw-routing-cdk", + "projectFolder": "cloudfront-keyvaluestore-apigw-routing-cdk", + "templateFile": "lib/pattern-stack.ts" + } + }, + "resources": { + "bullets": [ + { + "text": "AWS Well-Architected Guide - Cell Partition", + "link": "https://docs.aws.amazon.com/wellarchitected/latest/reducing-scope-of-impact-with-cell-based-architecture/cell-partition.html" + }, + { + "text": "CloudFront Key Value Store Documentation", + "link": "https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/kvs-with-functions.html" + }, + { + "text": "CloudFront Functions Documentation", + "link": "https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/cloudfront-functions.html" + } + ] + }, + "deploy": { + "text": [ + "Clone the repository: git clone https://github.com/aws-samples/serverless-patterns", + "Change directory: cd cloudfront-keyvaluestore-apigw-routing-cdk", + "Install dependencies: npm install", + "Deploy the CDK stack: cdk deploy" + ] + }, + "testing": { + "text": [ + "1. Get Key Value Store ETAG: aws cloudfront-keyvaluestore describe-key-value-store --kvs-arn=[KVSTOREARN]", + "2. Add entries to the Key Value Store: aws cloudfront-keyvaluestore update-keys --kvs-arn=[KVSTOREARN] --if-match=[ETAG] --puts '[{\"Key\": \"APIGW1URL\", \"Value\": \"[APIGATEWAY1URL]\"},{\"Key\": \"APIGW2URL\", \"Value\": \"[APIGATEWAY2URL]\"}]'", + "3. Access CloudFront URL: curl -i -L [CLOUDFRONTDOMAINNAME]", + "4. The request should be redirected to either API Gateway 1 or API Gateway 2, showing a response like {\"message\": \"Hello from API 1\"} or {\"message\": \"Hello from API 2\"}", + "5. Make multiple requests to observe routing between the two API Gateway endpoints." + ] + }, + "cleanup": { + "text": [ + "Delete the stack: cdk destroy" + ] + }, + "authors": [ + { + "name": "Marco Jahn", + "image": "https://sessionize.com/image/e99b-400o400o2-pqR4BacUSzHrq4fgZ4wwEQ.png", + "bio": "Senior Solutions Architect, Amazon Web Services", + "linkedin": "marcojahn" + } + ], + "patternArch": { + "icon1": { + "x": 20, + "y": 50, + "service": "cloudfront", + "label": "Amazon CloudFront" + }, + "icon2": { + "x": 50, + "y": 50, + "service": "cf-functions", + "label": "CloudFront function" + }, + "icon3": { + "x": 80, + "y": 20, + "service": "apigw", + "label": "Amazon API Gateway 1" + }, + "icon4": { + "x": 80, + "y": 70, + "service": "apigw", + "label": "Amazon API Gateway 2" + }, + "line1": { + "from": "icon1", + "to": "icon2", + "label": "" + }, + "line2": { + "from": "icon2", + "to": "icon3", + "label": "" + }, + "line3": { + "from": "icon2", + "to": "icon4", + "label": "" + } + } +}