├── .gitignore ├── License.md ├── Readme.md ├── Taskfile.yml ├── dynamodb-crud ├── .gitignore ├── .npmignore ├── README.md ├── Taskfile.yml ├── bin │ └── dynamodb-crud.ts ├── cdk.json ├── events │ ├── create.json │ ├── createWithOwner.json │ ├── delete.json │ ├── getOne.json │ ├── query.json │ └── update.json ├── jest.config.js ├── lambda-fns │ ├── create │ │ ├── index.ts │ │ ├── package-lock.json │ │ └── package.json │ ├── delete │ │ ├── index.ts │ │ ├── package-lock.json │ │ └── package.json │ ├── getAll │ │ ├── index.ts │ │ ├── package-lock.json │ │ └── package.json │ ├── getOne │ │ ├── index.ts │ │ ├── package-lock.json │ │ └── package.json │ ├── query │ │ ├── index.ts │ │ ├── package-lock.json │ │ └── package.json │ └── update │ │ ├── index.ts │ │ ├── package-lock.json │ │ └── package.json ├── lib │ ├── dynamodb-crud-stack.ts │ └── variables.ts ├── package-lock.json ├── package.json ├── test │ └── dynamodb-crud.test.ts └── tsconfig.json ├── go-lambda ├── .gitignore ├── .npmignore ├── README.md ├── Taskfile.yml ├── bin │ └── go-lambda.ts ├── cdk.json ├── jest.config.js ├── lambda-fns │ └── hello-world │ │ ├── go.mod │ │ ├── go.sum │ │ └── main.go ├── lib │ └── go-lambda-stack.ts ├── package-lock.json ├── package.json ├── test │ └── go-lambda.test.ts └── tsconfig.json ├── http-api ├── .gitignore ├── .npmignore ├── README.md ├── Taskfile.yml ├── bin │ └── http-api.ts ├── cdk.json ├── jest.config.js ├── lambda-fns │ └── sign-up │ │ ├── Taskfile.yml │ │ ├── deployment.zip │ │ └── index.js ├── lib │ ├── cloudfront-http-api-stack.ts │ ├── http-api-stack.ts │ └── variables.ts ├── package-lock.json ├── package.json ├── test │ └── http-api.test.ts └── tsconfig.json ├── https-redirect ├── .gitignore ├── .npmignore ├── README.md ├── Taskfile.yml ├── bin │ └── https-redirect.ts ├── cdk.json ├── jest.config.js ├── lib │ └── https-redirect-stack.ts ├── package-lock.json ├── package.json ├── test │ └── https-redirect.test.ts └── tsconfig.json ├── hugo-pipeline ├── Readme.md ├── backend-infra │ ├── .gitignore │ ├── .npmignore │ ├── README.md │ ├── Taskfile.yml │ ├── bin │ │ └── backend-infra.ts │ ├── cdk.json │ ├── jest.config.js │ ├── lib │ │ ├── backend-infra-stack.ts │ │ ├── certificate.ts │ │ ├── code-pipeline.ts │ │ ├── s3cloudfront.ts │ │ └── variables.ts │ ├── package-lock.json │ ├── package.json │ ├── test │ │ └── backend-infra.test.ts │ └── tsconfig.json └── hugo-website │ ├── archetypes │ └── default.md │ ├── buildspec.yml │ ├── config.toml │ ├── content │ └── .gitkeep │ ├── data │ └── .gitkeep │ ├── layouts │ └── .gitkeep │ ├── static │ └── .gitkeep │ └── themes │ └── .gitkeep ├── lambda-cost ├── .gitignore ├── .npmignore ├── README.md ├── Taskfile.yml ├── bin │ └── lambda-cost.ts ├── cdk.json ├── jest.config.js ├── lambda-fns │ ├── create-todo │ │ ├── index.ts │ │ ├── package-lock.json │ │ └── package.json │ ├── delete-todo │ │ ├── index.ts │ │ ├── package-lock.json │ │ └── package.json │ └── get-todo │ │ ├── index.ts │ │ ├── package-lock.json │ │ └── package.json ├── lib │ ├── lambda-cost-stack.ts │ └── variables.ts ├── package-lock.json ├── package.json ├── test │ └── lambda-cost.test.ts └── tsconfig.json ├── lambda-layers ├── .gitignore ├── .npmignore ├── README.md ├── Taskfile.yml ├── bin │ └── lambda-layers.ts ├── cdk.json ├── jest.config.js ├── lambda-fns │ ├── layer │ │ ├── Taskfile.yml │ │ ├── deployment.zip │ │ └── nodejs │ │ │ └── discount.js │ ├── one │ │ ├── Taskfile.yml │ │ ├── deployment.zip │ │ └── index.js │ └── two │ │ ├── Taskfile.yml │ │ ├── deployment.zip │ │ └── index.js ├── lib │ ├── lambda-layers-stack.ts │ └── variables.ts ├── package-lock.json ├── package.json ├── test │ └── lambda-layers.test.ts └── tsconfig.json ├── lambda-local ├── .gitignore ├── .npmignore ├── README.md ├── Taskfile.yml ├── bin │ └── lambda-local.ts ├── cdk.json ├── events │ └── hello.json ├── jest.config.js ├── lambda-fns │ └── one │ │ ├── Taskfile.yml │ │ ├── deployment.zip │ │ └── index.js ├── lib │ ├── lambda-local-stack.ts │ └── variables.ts ├── package-lock.json ├── package.json ├── test │ └── lambda-local.test.ts └── tsconfig.json ├── nextjs-pipeline ├── Readme.md ├── backend-infra │ ├── .gitignore │ ├── .npmignore │ ├── README.md │ ├── Taskfile.yml │ ├── bin │ │ └── backend-infra.ts │ ├── cdk.json │ ├── jest.config.js │ ├── lib │ │ ├── backend-infra-stack.ts │ │ ├── variables.ts │ │ └── witout-load-balancer.ts │ ├── package-lock.json │ ├── package.json │ ├── test │ │ └── backend-infra.test.ts │ └── tsconfig.json └── next-client │ ├── .gitignore │ ├── Dockerfile │ ├── README.md │ ├── buildspec.yml │ ├── package-lock.json │ ├── package.json │ ├── pages │ ├── _app.js │ ├── api │ │ └── hello.js │ └── index.js │ ├── public │ ├── favicon.ico │ └── vercel.svg │ └── styles │ ├── Home.module.css │ └── globals.css ├── password-protect-s3-static-site ├── .gitignore ├── .npmignore ├── README.md ├── Taskfile.yml ├── bin │ └── password-protect-s3-static-site.ts ├── cdk.json ├── jest.config.js ├── lambda-fns │ └── basic-auth │ │ ├── Taskfile.yml │ │ └── index.js ├── lib │ ├── password-protect-s3-static-site-stack.ts │ └── variables.ts ├── package-lock.json ├── package.json ├── test │ └── password-protect-s3-static-site.test.ts └── tsconfig.json ├── react-pipeline ├── Readme.md ├── backend-infra │ ├── .gitignore │ ├── .npmignore │ ├── README.md │ ├── Taskfile.yml │ ├── bin │ │ └── backend-infra.ts │ ├── cdk.json │ ├── jest.config.js │ ├── lib │ │ ├── backend-infra-stack.ts │ │ ├── certificate.ts │ │ ├── code-pipeline.ts │ │ ├── s3cloudfront.ts │ │ └── variables.ts │ ├── package-lock.json │ ├── package.json │ ├── test │ │ └── backend-infra.test.ts │ └── tsconfig.json └── react-client │ ├── .gitignore │ ├── README.md │ ├── buildspec.yml │ ├── package-lock.json │ ├── package.json │ ├── public │ ├── favicon.ico │ ├── index.html │ ├── logo192.png │ ├── logo512.png │ ├── manifest.json │ └── robots.txt │ ├── src │ ├── App.css │ ├── App.test.tsx │ ├── App.tsx │ ├── index.css │ ├── index.tsx │ ├── logo.svg │ ├── react-app-env.d.ts │ ├── reportWebVitals.ts │ └── setupTests.ts │ └── tsconfig.json └── ts-lambda ├── .gitignore ├── .npmignore ├── README.md ├── Taskfile.yml ├── bin └── ts-lambda.ts ├── cdk.json ├── jest.config.js ├── lambda-fns └── hello-world │ ├── index.ts │ ├── package-lock.json │ └── package.json ├── lib └── ts-lambda-stack.ts ├── package-lock.json ├── package.json ├── test └── ts-lambda.test.ts └── tsconfig.json /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.toptal.com/developers/gitignore/api/node,windows,macos,linux 3 | # Edit at https://www.toptal.com/developers/gitignore?templates=node,windows,macos,linux 4 | 5 | ### Linux ### 6 | *~ 7 | 8 | # temporary files which can be created if a process still has a handle open of a deleted file 9 | .fuse_hidden* 10 | 11 | # KDE directory preferences 12 | .directory 13 | 14 | # Linux trash folder which might appear on any partition or disk 15 | .Trash-* 16 | 17 | # .nfs files are created when an open file is removed but is still being accessed 18 | .nfs* 19 | 20 | ### macOS ### 21 | # General 22 | .DS_Store 23 | .AppleDouble 24 | .LSOverride 25 | 26 | # Icon must end with two \r 27 | Icon 28 | 29 | 30 | # Thumbnails 31 | ._* 32 | 33 | # Files that might appear in the root of a volume 34 | .DocumentRevisions-V100 35 | .fseventsd 36 | .Spotlight-V100 37 | .TemporaryItems 38 | .Trashes 39 | .VolumeIcon.icns 40 | .com.apple.timemachine.donotpresent 41 | 42 | # Directories potentially created on remote AFP share 43 | .AppleDB 44 | .AppleDesktop 45 | Network Trash Folder 46 | Temporary Items 47 | .apdisk 48 | 49 | ### Node ### 50 | # Logs 51 | logs 52 | *.log 53 | npm-debug.log* 54 | yarn-debug.log* 55 | yarn-error.log* 56 | lerna-debug.log* 57 | 58 | # Diagnostic reports (https://nodejs.org/api/report.html) 59 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 60 | 61 | # Runtime data 62 | pids 63 | *.pid 64 | *.seed 65 | *.pid.lock 66 | 67 | # Directory for instrumented libs generated by jscoverage/JSCover 68 | lib-cov 69 | 70 | # Coverage directory used by tools like istanbul 71 | coverage 72 | *.lcov 73 | 74 | # nyc test coverage 75 | .nyc_output 76 | 77 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 78 | .grunt 79 | 80 | # Bower dependency directory (https://bower.io/) 81 | bower_components 82 | 83 | # node-waf configuration 84 | .lock-wscript 85 | 86 | # Compiled binary addons (https://nodejs.org/api/addons.html) 87 | build/Release 88 | 89 | # Dependency directories 90 | node_modules/ 91 | jspm_packages/ 92 | 93 | # TypeScript v1 declaration files 94 | typings/ 95 | 96 | # TypeScript cache 97 | *.tsbuildinfo 98 | 99 | # Optional npm cache directory 100 | .npm 101 | 102 | # Optional eslint cache 103 | .eslintcache 104 | 105 | # Microbundle cache 106 | .rpt2_cache/ 107 | .rts2_cache_cjs/ 108 | .rts2_cache_es/ 109 | .rts2_cache_umd/ 110 | 111 | # Optional REPL history 112 | .node_repl_history 113 | 114 | # Output of 'npm pack' 115 | *.tgz 116 | 117 | # Yarn Integrity file 118 | .yarn-integrity 119 | 120 | # dotenv environment variables file 121 | .env 122 | .env.test 123 | .env*.local 124 | 125 | # parcel-bundler cache (https://parceljs.org/) 126 | .cache 127 | .parcel-cache 128 | 129 | # Next.js build output 130 | .next 131 | 132 | # Nuxt.js build / generate output 133 | .nuxt 134 | dist 135 | 136 | # Gatsby files 137 | .cache/ 138 | # Comment in the public line in if your project uses Gatsby and not Next.js 139 | # https://nextjs.org/blog/next-9-1#public-directory-support 140 | # public 141 | 142 | # vuepress build output 143 | .vuepress/dist 144 | 145 | # Serverless directories 146 | .serverless/ 147 | 148 | # FuseBox cache 149 | .fusebox/ 150 | 151 | # DynamoDB Local files 152 | .dynamodb/ 153 | 154 | # TernJS port file 155 | .tern-port 156 | 157 | # Stores VSCode versions used for testing VSCode extensions 158 | .vscode-test 159 | 160 | ### Windows ### 161 | # Windows thumbnail cache files 162 | Thumbs.db 163 | Thumbs.db:encryptable 164 | ehthumbs.db 165 | ehthumbs_vista.db 166 | 167 | # Dump file 168 | *.stackdump 169 | 170 | # Folder config file 171 | [Dd]esktop.ini 172 | 173 | # Recycle Bin used on file shares 174 | $RECYCLE.BIN/ 175 | 176 | # Windows Installer files 177 | *.cab 178 | *.msi 179 | *.msix 180 | *.msm 181 | *.msp 182 | 183 | # Windows shortcuts 184 | *.lnk 185 | 186 | # End of https://www.toptal.com/developers/gitignore/api/node,windows,macos,linux -------------------------------------------------------------------------------- /License.md: -------------------------------------------------------------------------------- 1 | # MIT License 2 | 3 | Copyright (c) 2020-2021 Apoorv Mote 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # AWS CDK examples 2 | 3 | [![MIT License](https://badgen.now.sh/badge/License/MIT/blue)](https://github.com/apoorvmote/cdk-examples/blob/master/License.md) 4 | ![Typescript](https://badgen.net/badge/icon/typescript?icon=typescript&label) 5 | 6 | These are CDK examples exclusively written in typescript. These are CDK template/patterns that I came across my journey with AWS CDK. I hope other people find it helpful as I myself refer to these examples time to time. 7 | 8 | Detailed explanation of each cdk example will be found at 9 | 10 | ## [https redirect](https://apoorv.blog/redirect-www-to-non-www/) 11 | 12 | Redirect from www to non-www url 13 | 14 | ## [React pipeline](https://apoorv.blog/deploy-reactjs-cloudfront-codepipeline-cdk/) 15 | 16 | Build and deploy react app on s3 and cloudfront with Codepipeline 17 | 18 | ## [Hugo pipeline](https://apoorv.blog/deploy-hugo-cloudfront-codepipeline-cdk/) 19 | 20 | Build and deploy hugo website on s3 and cloudfront with Codepipeline 21 | 22 | ## [NextJS pipeline](https://apoorv.blog/nextjs-fargate-codepipline-cdk/) 23 | 24 | Build docker container and deploy SSR NextJS app on Fargate with ECS and Codepipeline 25 | 26 | ## [Lambda Layers](https://apoorv.blog/lambda-layers-cdk/) 27 | 28 | Share code and dependency library between multiple lambda functions with layers 29 | 30 | ## [Lambda Local](https://apoorv.blog/run-lambda-locally-cdk-sam/) 31 | 32 | Lambda that is deployed with CDK can be invoked locally with SAM for faster iteration. 33 | 34 | ## [HTTP API](https://apoorv.blog/http-api-cloudfront-cdk/) 35 | 36 | We will deploy HTTP API with CDK and you can consume api via subdomain or via main domain. 37 | 38 | ## [Typescript Lambda](https://apoorv.blog/typescript-lambda-cdk/) 39 | 40 | Lambda function written in typescript. It is compiled and build to javascript and deployed with CDK. 41 | 42 | ## [Dynamodb Crud Lambda](https://apoorv.blog/dynamodb-crud-typescript-lambda/) 43 | 44 | We are using latest AWS SDK V3 for Javascript for doing CRUD operations on Dynamodb with Lambda functions written in typescript. 45 | 46 | ## [Golang Lambda](https://apoorv.blog/golang-lambda-cdk/) 47 | 48 | Build golang lambda function inside docker container that matches closely with the production environment. 49 | 50 | ## [Lambda Cost Optimization](https://apoorv.blog/optimize-lambda-cost/) 51 | 52 | We are going to run lambda on multiple ram settings to find out which gives you better value for money :moneybag:. 53 | 54 | ## [Basic auth to Password protect s3 website with cloudfront lambda@edge](https://apoorv.blog/password-protect-s3-static-site/) 55 | 56 | Before deploying changed to production website we deploy changes to preview site and test. -------------------------------------------------------------------------------- /Taskfile.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | vars: 4 | COMMIT_MESSAGE: updated to 2.38.1 5 | 6 | tasks: 7 | default: 8 | cmds: 9 | - git add . 10 | - git commit -m "{{.COMMIT_MESSAGE}}" 11 | - git push origin master -------------------------------------------------------------------------------- /dynamodb-crud/.gitignore: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /dynamodb-crud/.npmignore: -------------------------------------------------------------------------------- 1 | *.ts 2 | !*.d.ts 3 | 4 | # CDK asset staging directory 5 | .cdk.staging 6 | cdk.out 7 | -------------------------------------------------------------------------------- /dynamodb-crud/README.md: -------------------------------------------------------------------------------- 1 | # [Dynamodb Crud Lambda](https://apoorv.blog/dynamodb-crud-typescript-lambda/) 2 | 3 | [![Read Tutorial](https://badgen.now.sh/badge/Read/Tutorial/purple)](https://apoorv.blog/dynamodb-crud-typescript-lambda/) 4 | [![MIT License](https://badgen.now.sh/badge/License/MIT/blue)](https://github.com/apoorvmote/cdk-examples/blob/master/License.md) 5 | ![AWS CDK 2.38.1](https://badgen.net/badge/aws-cdk/2.38.1/yellow) 6 | ![Typescript](https://badgen.net/badge/icon/typescript?icon=typescript&label) 7 | 8 | We are using latest AWS SDK V3 for Javascript for doing CRUD operations on Dynamodb with Lambda functions written in typescript -------------------------------------------------------------------------------- /dynamodb-crud/Taskfile.yml: -------------------------------------------------------------------------------- 1 | # https://taskfile.dev 2 | 3 | version: '3' 4 | 5 | tasks: 6 | update: 7 | cmds: 8 | - rm -rf node_modules/ 9 | - npm i 10 | fix: 11 | cmds: 12 | - npm audit fix -------------------------------------------------------------------------------- /dynamodb-crud/bin/dynamodb-crud.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import 'source-map-support/register'; 3 | import * as cdk from 'aws-cdk-lib'; 4 | import { DynamodbCrudStack } from '../lib/dynamodb-crud-stack'; 5 | 6 | const app = new cdk.App(); 7 | new DynamodbCrudStack(app, 'DynamodbCrudStack-AnBmdHH53PUgeF7R'); 8 | -------------------------------------------------------------------------------- /dynamodb-crud/cdk.json: -------------------------------------------------------------------------------- 1 | { 2 | "app": "npx ts-node --prefer-ts-exts bin/dynamodb-crud.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-apigateway:usagePlanKeyOrderInsensitiveId": true, 21 | "@aws-cdk/core:stackRelativeExports": true, 22 | "@aws-cdk/aws-rds:lowercaseDbIdentifier": true, 23 | "@aws-cdk/aws-lambda:recognizeVersionProps": true, 24 | "@aws-cdk/aws-cloudfront:defaultSecurityPolicyTLSv1.2_2021": true 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /dynamodb-crud/events/create.json: -------------------------------------------------------------------------------- 1 | { 2 | "body": "{ \"title\": \"get milk\", \"done\": false }" 3 | } -------------------------------------------------------------------------------- /dynamodb-crud/events/createWithOwner.json: -------------------------------------------------------------------------------- 1 | { 2 | "body": "{ \"title\": \"get coffee\", \"owner\": \"1212135c-1e2a-44e4-96c4-d41ff9952a4d\", \"done\": false }" 3 | } -------------------------------------------------------------------------------- /dynamodb-crud/events/delete.json: -------------------------------------------------------------------------------- 1 | { 2 | "body": "{ \"id\": \"c67f1017-1a63-4fba-97db-3dd0f1434fc0\" }" 3 | } -------------------------------------------------------------------------------- /dynamodb-crud/events/getOne.json: -------------------------------------------------------------------------------- 1 | { 2 | "body": "{ \"id\": \"c67f1017-1a63-4fba-97db-3dd0f1434fc0\" }" 3 | } -------------------------------------------------------------------------------- /dynamodb-crud/events/query.json: -------------------------------------------------------------------------------- 1 | { 2 | "body": "{ \"owner\": \"1212135c-1e2a-44e4-96c4-d41ff9952a4d\" }" 3 | } -------------------------------------------------------------------------------- /dynamodb-crud/events/update.json: -------------------------------------------------------------------------------- 1 | { 2 | "body": "{ \"id\": \"c67f1017-1a63-4fba-97db-3dd0f1434fc0\", \"done\": true }" 3 | } -------------------------------------------------------------------------------- /dynamodb-crud/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | roots: ['/test'], 3 | testMatch: ['**/*.test.ts'], 4 | transform: { 5 | '^.+\\.tsx?$': 'ts-jest' 6 | } 7 | }; 8 | -------------------------------------------------------------------------------- /dynamodb-crud/lambda-fns/create/index.ts: -------------------------------------------------------------------------------- 1 | import { APIGatewayProxyEventV2, APIGatewayProxyResultV2 } from "aws-lambda"; 2 | import { DynamoDB, PutItemInput } from '@aws-sdk/client-dynamodb' 3 | import { marshall } from '@aws-sdk/util-dynamodb' 4 | import { v4 as uuid } from 'uuid' 5 | 6 | interface TodoInput { 7 | id?: string 8 | owner?: string 9 | title: string 10 | done: boolean 11 | } 12 | 13 | interface Todo { 14 | id: string 15 | owner?: string 16 | title: string 17 | done: boolean 18 | } 19 | 20 | export async function createTodo(event: APIGatewayProxyEventV2): Promise { 21 | 22 | const { body } = event 23 | 24 | if (!body) { 25 | 26 | return sendFail('invalid request') 27 | } 28 | 29 | const { id, owner, title, done } = JSON.parse(body) as TodoInput 30 | 31 | const dynamoClient = new DynamoDB({ 32 | region: 'us-east-1' 33 | }) 34 | 35 | const newTodo: Todo = { 36 | id: id ?? uuid(), 37 | owner, title, done 38 | } 39 | 40 | const todoParams: PutItemInput = { 41 | Item: marshall(newTodo), 42 | TableName: process.env.TODO_TABLE_NAME 43 | } 44 | 45 | try { 46 | 47 | await dynamoClient.putItem(todoParams) 48 | 49 | return { 50 | statusCode: 200, 51 | body: JSON.stringify({ newTodo }) 52 | } 53 | 54 | } catch (err) { 55 | 56 | console.log(err) 57 | 58 | return sendFail('something went wrong') 59 | } 60 | } 61 | 62 | function sendFail(message: string): APIGatewayProxyResultV2 { 63 | 64 | return { 65 | statusCode: 400, 66 | body: JSON.stringify({ message }) 67 | } 68 | } -------------------------------------------------------------------------------- /dynamodb-crud/lambda-fns/create/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "create", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "@aws-sdk/client-dynamodb": "^3.13.0", 14 | "@aws-sdk/util-dynamodb": "^3.13.0", 15 | "uuid": "^8.3.2" 16 | }, 17 | "devDependencies": { 18 | "@types/aws-lambda": "^8.10.75", 19 | "@types/node": "^14.14.41", 20 | "@types/uuid": "^8.3.0", 21 | "typescript": "^4.2.4" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /dynamodb-crud/lambda-fns/delete/index.ts: -------------------------------------------------------------------------------- 1 | import { APIGatewayProxyEventV2, APIGatewayProxyResultV2 } from "aws-lambda"; 2 | import { DynamoDB, DeleteItemInput } from '@aws-sdk/client-dynamodb' 3 | import { marshall, unmarshall } from '@aws-sdk/util-dynamodb' 4 | 5 | interface DeleteTodo { 6 | id: string 7 | } 8 | 9 | export async function deleteTodo(event: APIGatewayProxyEventV2): Promise { 10 | 11 | const { body } = event 12 | 13 | if (!body) { 14 | 15 | return sendFail('invalid request') 16 | } 17 | 18 | const { id } = JSON.parse(body) as DeleteTodo 19 | 20 | const dynamoClient = new DynamoDB({ 21 | region: 'us-east-1' 22 | }) 23 | 24 | const todoParams: DeleteItemInput = { 25 | Key: marshall({ id }), 26 | ReturnValues: 'ALL_OLD', 27 | TableName: process.env.TODO_TABLE_NAME 28 | } 29 | 30 | try { 31 | 32 | const { Attributes } = await dynamoClient.deleteItem(todoParams) 33 | 34 | const todo = Attributes ? unmarshall(Attributes) : null 35 | 36 | return { 37 | statusCode: 200, 38 | body: JSON.stringify({ todo }) 39 | } 40 | 41 | } catch (err) { 42 | 43 | console.log(err) 44 | 45 | return sendFail('something went wrong') 46 | } 47 | } 48 | 49 | function sendFail(message: string): APIGatewayProxyResultV2 { 50 | 51 | return { 52 | statusCode: 400, 53 | body: JSON.stringify({ message }) 54 | } 55 | } -------------------------------------------------------------------------------- /dynamodb-crud/lambda-fns/delete/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "delete", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "@aws-sdk/client-dynamodb": "^3.1.0", 14 | "@aws-sdk/util-dynamodb": "^3.1.0" 15 | }, 16 | "devDependencies": { 17 | "@types/aws-lambda": "^8.10.68", 18 | "@types/node": "^14.14.19", 19 | "typescript": "^4.1.3" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /dynamodb-crud/lambda-fns/getAll/index.ts: -------------------------------------------------------------------------------- 1 | import { APIGatewayProxyEventV2, APIGatewayProxyResultV2 } from "aws-lambda"; 2 | import { DynamoDB, ScanInput } from '@aws-sdk/client-dynamodb' 3 | import { unmarshall } from '@aws-sdk/util-dynamodb' 4 | 5 | export async function getAll(event: APIGatewayProxyEventV2): Promise { 6 | 7 | const dynamoClient = new DynamoDB({ 8 | region: 'us-east-1' 9 | }) 10 | 11 | const scanTodo: ScanInput = { 12 | TableName: process.env.TODO_TABLE_NAME 13 | } 14 | 15 | try { 16 | 17 | const { Items } = await dynamoClient.scan(scanTodo) 18 | 19 | const userData = Items ? Items.map(item => unmarshall(item)) : [] 20 | 21 | return { 22 | statusCode: 200, 23 | body: JSON.stringify({ listTodo: userData}) 24 | } 25 | 26 | } catch (err) { 27 | 28 | console.log(err) 29 | 30 | return { 31 | statusCode: 400, 32 | body: JSON.stringify({ 33 | message: 'something went wrong' 34 | }) 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /dynamodb-crud/lambda-fns/getAll/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "getAll", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "@aws-sdk/client-dynamodb": "^3.1.0", 14 | "@aws-sdk/util-dynamodb": "^3.1.0" 15 | }, 16 | "devDependencies": { 17 | "@types/aws-lambda": "^8.10.68", 18 | "@types/node": "^14.14.19", 19 | "typescript": "^4.1.3" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /dynamodb-crud/lambda-fns/getOne/index.ts: -------------------------------------------------------------------------------- 1 | import { APIGatewayProxyEventV2, APIGatewayProxyResultV2 } from "aws-lambda"; 2 | import { DynamoDB, GetItemInput } from '@aws-sdk/client-dynamodb' 3 | import { marshall, unmarshall } from '@aws-sdk/util-dynamodb' 4 | 5 | interface UserInput { 6 | id: string 7 | } 8 | 9 | export async function getOne(event: APIGatewayProxyEventV2): Promise { 10 | 11 | const { body } = event 12 | 13 | if (!body) return sendError('invalid request') 14 | 15 | const data = JSON.parse(body) as UserInput 16 | 17 | const dynamoClient = new DynamoDB({ 18 | region: 'us-east-1' 19 | }) 20 | 21 | const getTodo: GetItemInput = { 22 | Key: marshall({ 23 | id: data.id 24 | }), 25 | TableName: process.env.TODO_TABLE_NAME 26 | } 27 | 28 | try { 29 | 30 | const { Item } = await dynamoClient.getItem(getTodo) 31 | 32 | const todo = Item ? unmarshall(Item) : null 33 | 34 | return { 35 | statusCode: 200, 36 | body: JSON.stringify({ todo }) 37 | } 38 | 39 | } catch (err) { 40 | 41 | console.log(err) 42 | 43 | return sendError('something went wrong') 44 | } 45 | } 46 | 47 | function sendError(message: string): APIGatewayProxyResultV2 { 48 | 49 | return { 50 | statusCode: 400, 51 | body: JSON.stringify({ message }) 52 | } 53 | } -------------------------------------------------------------------------------- /dynamodb-crud/lambda-fns/getOne/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "getOne", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "@aws-sdk/client-dynamodb": "^3.1.0", 14 | "@aws-sdk/util-dynamodb": "^3.1.0" 15 | }, 16 | "devDependencies": { 17 | "@types/aws-lambda": "^8.10.68", 18 | "@types/node": "^14.14.19", 19 | "typescript": "^4.1.3" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /dynamodb-crud/lambda-fns/query/index.ts: -------------------------------------------------------------------------------- 1 | import { APIGatewayProxyEventV2, APIGatewayProxyResultV2 } from "aws-lambda"; 2 | import { DynamoDB, QueryInput } from '@aws-sdk/client-dynamodb' 3 | import { marshall, unmarshall } from '@aws-sdk/util-dynamodb' 4 | 5 | interface UserInput { 6 | owner: string 7 | } 8 | 9 | export async function queryTodo(event: APIGatewayProxyEventV2): Promise { 10 | 11 | const { body } = event 12 | 13 | if (!body) return sendError('invalid request') 14 | 15 | const data = JSON.parse(body) as UserInput 16 | 17 | const dynamoClient = new DynamoDB({ 18 | region: 'us-east-1' 19 | }) 20 | 21 | const queryTodo: QueryInput = { 22 | KeyConditionExpression: '#todoOwner = :userId', 23 | ExpressionAttributeNames: { 24 | '#todoOwner': 'owner' 25 | }, 26 | ExpressionAttributeValues: marshall({ 27 | ':userId': data.owner 28 | }), 29 | IndexName: 'ownerIndex', 30 | TableName: process.env.TODO_TABLE_NAME 31 | } 32 | 33 | try { 34 | 35 | const { Items } = await dynamoClient.query(queryTodo) 36 | 37 | const listTodo = Items ? Items.map(item => unmarshall(item)) : [] 38 | 39 | return { 40 | statusCode: 200, 41 | body: JSON.stringify({ listTodo }) 42 | } 43 | 44 | } catch (err) { 45 | 46 | console.log(err) 47 | 48 | return sendError('something went wrong') 49 | } 50 | } 51 | 52 | function sendError(message: string): APIGatewayProxyResultV2 { 53 | 54 | return { 55 | statusCode: 400, 56 | body: JSON.stringify({ message }) 57 | } 58 | } -------------------------------------------------------------------------------- /dynamodb-crud/lambda-fns/query/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "query", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "@aws-sdk/client-dynamodb": "^3.1.0", 14 | "@aws-sdk/util-dynamodb": "^3.1.0" 15 | }, 16 | "devDependencies": { 17 | "@types/aws-lambda": "^8.10.68", 18 | "@types/node": "^14.14.19", 19 | "typescript": "^4.1.3" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /dynamodb-crud/lambda-fns/update/index.ts: -------------------------------------------------------------------------------- 1 | import { APIGatewayProxyEventV2, APIGatewayProxyResultV2 } from "aws-lambda"; 2 | import { DynamoDB, UpdateItemInput } from '@aws-sdk/client-dynamodb' 3 | import { marshall, unmarshall } from '@aws-sdk/util-dynamodb' 4 | 5 | interface UpdateTodo { 6 | id: string 7 | done: boolean 8 | } 9 | 10 | export async function update(event: APIGatewayProxyEventV2): Promise { 11 | 12 | const { body } = event 13 | 14 | if (!body) { 15 | 16 | return sendFail('invalid request') 17 | } 18 | 19 | const { id, done } = JSON.parse(body) as UpdateTodo 20 | 21 | const dynamoClient = new DynamoDB({ 22 | region: 'us-east-1' 23 | }) 24 | 25 | const todoParams: UpdateItemInput = { 26 | Key: marshall({ id }), 27 | UpdateExpression: 'set done = :done', 28 | ExpressionAttributeValues: marshall({ 29 | ':done': done 30 | }), 31 | ReturnValues: 'ALL_NEW', 32 | TableName: process.env.UPDATE_TABLE_NAME 33 | } 34 | 35 | try { 36 | 37 | const { Attributes } = await dynamoClient.updateItem(todoParams) 38 | 39 | const todo = Attributes ? unmarshall(Attributes) : null 40 | 41 | return { 42 | statusCode: 200, 43 | body: JSON.stringify({ todo }) 44 | } 45 | 46 | } catch (err) { 47 | 48 | console.log(err) 49 | 50 | return sendFail('something went wrong') 51 | } 52 | } 53 | 54 | function sendFail(message: string): APIGatewayProxyResultV2 { 55 | 56 | return { 57 | statusCode: 200, 58 | body: JSON.stringify({ message }) 59 | } 60 | } -------------------------------------------------------------------------------- /dynamodb-crud/lambda-fns/update/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "update", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "@aws-sdk/client-dynamodb": "^3.1.0", 14 | "@aws-sdk/util-dynamodb": "^3.1.0" 15 | }, 16 | "devDependencies": { 17 | "@types/aws-lambda": "^8.10.68", 18 | "@types/node": "^14.14.19", 19 | "typescript": "^4.1.3" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /dynamodb-crud/lib/dynamodb-crud-stack.ts: -------------------------------------------------------------------------------- 1 | import { Stack, StackProps, CfnOutput, RemovalPolicy } from "aws-cdk-lib" 2 | import { Construct } from "constructs" 3 | import { AttributeType, BillingMode, Table } from 'aws-cdk-lib/aws-dynamodb'; 4 | import { NodejsFunction } from 'aws-cdk-lib/aws-lambda-nodejs'; 5 | import { Architecture, Runtime } from 'aws-cdk-lib/aws-lambda'; 6 | import { todoTableName } from './variables' 7 | 8 | export class DynamodbCrudStack extends Stack { 9 | constructor(scope: Construct, id: string, props?: StackProps) { 10 | super(scope, id, props); 11 | 12 | // The code that defines your stack goes here 13 | const todoTable = new Table(this, 'todoTable', { 14 | partitionKey: { 15 | name: 'id', 16 | type: AttributeType.STRING 17 | }, 18 | billingMode: BillingMode.PAY_PER_REQUEST, 19 | removalPolicy: RemovalPolicy.DESTROY 20 | }) 21 | 22 | todoTable.addGlobalSecondaryIndex({ 23 | indexName: 'ownerIndex', 24 | partitionKey: { 25 | name: 'owner', 26 | type: AttributeType.STRING 27 | } 28 | }) 29 | 30 | new CfnOutput(this, 'todoTableName', { 31 | value: todoTable.tableName 32 | }) 33 | 34 | const createTodoFn = new NodejsFunction(this, 'createTodoFn', { 35 | runtime: Runtime.NODEJS_16_X, 36 | entry: `${__dirname}/../lambda-fns/create/index.ts`, 37 | handler: 'createTodo', 38 | architecture: Architecture.ARM_64, 39 | environment: { 40 | TODO_TABLE_NAME: todoTableName 41 | } 42 | }) 43 | 44 | todoTable.grantReadWriteData(createTodoFn) 45 | 46 | const getAllTodoFn = new NodejsFunction(this, 'getAllTodoFn', { 47 | runtime: Runtime.NODEJS_16_X, 48 | entry: `${__dirname}/../lambda-fns/getAll/index.ts`, 49 | handler: 'getAll', 50 | architecture: Architecture.ARM_64, 51 | environment: { 52 | TODO_TABLE_NAME: todoTableName 53 | } 54 | }) 55 | 56 | todoTable.grantReadData(getAllTodoFn) 57 | 58 | const getOneTodoFn = new NodejsFunction(this, 'getOneTodoFn', { 59 | runtime: Runtime.NODEJS_16_X, 60 | entry: `${__dirname}/../lambda-fns/getOne/index.ts`, 61 | handler: 'getOne', 62 | architecture: Architecture.ARM_64, 63 | environment: { 64 | TODO_TABLE_NAME: todoTableName 65 | } 66 | }) 67 | 68 | todoTable.grantReadData(getOneTodoFn) 69 | 70 | const updateTodoFn = new NodejsFunction(this, 'updateTodoFn', { 71 | runtime: Runtime.NODEJS_16_X, 72 | entry: `${__dirname}/../lambda-fns/update/index.ts`, 73 | handler: 'update', 74 | architecture: Architecture.ARM_64, 75 | environment: { 76 | TODO_TABLE_NAME: todoTableName 77 | } 78 | }) 79 | 80 | todoTable.grantReadWriteData(updateTodoFn) 81 | 82 | const deleteTodoFn = new NodejsFunction(this, 'deleteTodoFn', { 83 | runtime: Runtime.NODEJS_16_X, 84 | entry: `${__dirname}/../lambda-fns/delete/index.ts`, 85 | handler: 'deleteTodo', 86 | architecture: Architecture.ARM_64, 87 | environment: { 88 | TODO_TABLE_NAME: todoTableName 89 | } 90 | }) 91 | 92 | todoTable.grantReadWriteData(deleteTodoFn) 93 | 94 | const tableWithIndex = Table.fromTableAttributes(this, 'tableWithIndex', { 95 | tableName: todoTableName, 96 | globalIndexes: ['ownerIndex'] 97 | }) 98 | 99 | const queryTodoFn = new NodejsFunction(this, 'queryTodoFn', { 100 | runtime: Runtime.NODEJS_16_X, 101 | entry: `${__dirname}/../lambda-fns/query/index.ts`, 102 | handler: 'queryTodo', 103 | architecture: Architecture.ARM_64, 104 | environment: { 105 | TODO_TABLE_NAME: todoTableName 106 | } 107 | }) 108 | 109 | tableWithIndex.grantReadData(queryTodoFn) 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /dynamodb-crud/lib/variables.ts: -------------------------------------------------------------------------------- 1 | export const todoTableName = 'DynamodbCrudStack-AnBmdHH53PUgeF7R-todoTable' -------------------------------------------------------------------------------- /dynamodb-crud/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dynamodb-crud", 3 | "version": "1.1.0", 4 | "bin": { 5 | "dynamodb-crud": "bin/dynamodb-crud.js" 6 | }, 7 | "scripts": { 8 | "build": "tsc", 9 | "watch": "tsc -w", 10 | "test": "jest", 11 | "cdk": "cdk" 12 | }, 13 | "devDependencies": { 14 | "@types/jest": "^27.5.0", 15 | "@types/node": "10.17.27", 16 | "jest": "^27.5.1", 17 | "ts-jest": "^27.1.4", 18 | "aws-cdk": "2.38.1", 19 | "ts-node": "^10.7.0", 20 | "typescript": "~3.9.7" 21 | }, 22 | "dependencies": { 23 | "aws-cdk-lib": "2.38.1", 24 | "constructs": "^10.0.0", 25 | "esbuild": "^0.15.5", 26 | "source-map-support": "^0.5.21" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /dynamodb-crud/test/dynamodb-crud.test.ts: -------------------------------------------------------------------------------- 1 | import { expect as expectCDK, matchTemplate, MatchStyle } from '@aws-cdk/assert'; 2 | import * as cdk from '@aws-cdk/core'; 3 | import * as DynamodbCrud from '../lib/dynamodb-crud-stack'; 4 | 5 | test('Empty Stack', () => { 6 | const app = new cdk.App(); 7 | // WHEN 8 | const stack = new DynamodbCrud.DynamodbCrudStack(app, 'MyTestStack'); 9 | // THEN 10 | expectCDK(stack).to(matchTemplate({ 11 | "Resources": {} 12 | }, MatchStyle.EXACT)) 13 | }); 14 | -------------------------------------------------------------------------------- /dynamodb-crud/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2018", 4 | "module": "commonjs", 5 | "lib": ["es2018"], 6 | "declaration": true, 7 | "strict": true, 8 | "noImplicitAny": true, 9 | "strictNullChecks": true, 10 | "noImplicitThis": true, 11 | "alwaysStrict": true, 12 | "noUnusedLocals": false, 13 | "noUnusedParameters": false, 14 | "noImplicitReturns": true, 15 | "noFallthroughCasesInSwitch": false, 16 | "inlineSourceMap": true, 17 | "inlineSources": true, 18 | "experimentalDecorators": true, 19 | "strictPropertyInitialization": false, 20 | "typeRoots": ["./node_modules/@types"] 21 | }, 22 | "exclude": ["cdk.out"] 23 | } 24 | -------------------------------------------------------------------------------- /go-lambda/.gitignore: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /go-lambda/.npmignore: -------------------------------------------------------------------------------- 1 | *.ts 2 | !*.d.ts 3 | 4 | # CDK asset staging directory 5 | .cdk.staging 6 | cdk.out 7 | -------------------------------------------------------------------------------- /go-lambda/README.md: -------------------------------------------------------------------------------- 1 | # [Golang Lambda](https://apoorv.blog/golang-lambda-cdk/) 2 | 3 | [![Read Tutorial](https://badgen.now.sh/badge/Read/Tutorial/purple)](https://apoorv.blog/golang-lambda-cdk/) 4 | [![MIT License](https://badgen.now.sh/badge/License/MIT/blue)](https://github.com/apoorvmote/cdk-examples/blob/master/License.md) 5 | ![AWS CDK 2.38.1](https://badgen.net/badge/aws-cdk/2.38.1/yellow) 6 | ![Typescript](https://badgen.net/badge/icon/typescript?icon=typescript&label) 7 | 8 | Build golang lambda function inside docker container that matches closely with the production environment. -------------------------------------------------------------------------------- /go-lambda/Taskfile.yml: -------------------------------------------------------------------------------- 1 | # https://taskfile.dev 2 | 3 | version: '3' 4 | 5 | tasks: 6 | update: 7 | cmds: 8 | - rm -rf node_modules/ 9 | - npm i 10 | fix: 11 | cmds: 12 | - npm audit fix -------------------------------------------------------------------------------- /go-lambda/bin/go-lambda.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import 'source-map-support/register'; 3 | import * as cdk from 'aws-cdk-lib'; 4 | import { GoLambdaStack } from '../lib/go-lambda-stack'; 5 | 6 | const app = new cdk.App(); 7 | new GoLambdaStack(app, 'GoLambdaStack'); 8 | -------------------------------------------------------------------------------- /go-lambda/cdk.json: -------------------------------------------------------------------------------- 1 | { 2 | "app": "npx ts-node --prefer-ts-exts bin/go-lambda.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-apigateway:usagePlanKeyOrderInsensitiveId": true, 21 | "@aws-cdk/core:stackRelativeExports": true, 22 | "@aws-cdk/aws-rds:lowercaseDbIdentifier": true, 23 | "@aws-cdk/aws-lambda:recognizeVersionProps": true, 24 | "@aws-cdk/aws-cloudfront:defaultSecurityPolicyTLSv1.2_2021": true 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /go-lambda/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | roots: ['/test'], 3 | testMatch: ['**/*.test.ts'], 4 | transform: { 5 | '^.+\\.tsx?$': 'ts-jest' 6 | } 7 | }; 8 | -------------------------------------------------------------------------------- /go-lambda/lambda-fns/hello-world/go.mod: -------------------------------------------------------------------------------- 1 | module hello-world 2 | 3 | go 1.15 4 | 5 | require github.com/aws/aws-lambda-go v1.22.0 6 | -------------------------------------------------------------------------------- /go-lambda/lambda-fns/hello-world/go.sum: -------------------------------------------------------------------------------- 1 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 2 | github.com/aws/aws-lambda-go v1.22.0 h1:X7BKqIdfoJcbsEIi+Lrt5YjX1HnZexIbNWOQgkYKgfE= 3 | github.com/aws/aws-lambda-go v1.22.0/go.mod h1:jJmlefzPfGnckuHdXX7/80O3BvUUi12XOkbv4w9SGLU= 4 | github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= 5 | github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= 6 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 7 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 8 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 9 | github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 10 | github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= 11 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 12 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 13 | github.com/urfave/cli/v2 v2.2.0/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ= 14 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 15 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 16 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 17 | gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 18 | -------------------------------------------------------------------------------- /go-lambda/lambda-fns/hello-world/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/aws/aws-lambda-go/lambda" 5 | ) 6 | 7 | func main() { 8 | lambda.Start(handleRequest) 9 | } 10 | 11 | func handleRequest() (string, error) { 12 | 13 | return "hello from golang", nil 14 | } -------------------------------------------------------------------------------- /go-lambda/lib/go-lambda-stack.ts: -------------------------------------------------------------------------------- 1 | import { Stack, StackProps } from "aws-cdk-lib" 2 | import { Construct } from "constructs" 3 | import { Code, Function, Runtime } from 'aws-cdk-lib/aws-lambda'; 4 | 5 | export class GoLambdaStack extends Stack { 6 | constructor(scope: Construct, id: string, props?: StackProps) { 7 | super(scope, id, props); 8 | 9 | // The code that defines your stack goes here 10 | new Function(this, 'goLambda', { 11 | runtime: Runtime.GO_1_X, 12 | handler: 'main', 13 | code: Code.fromAsset(`${__dirname}/../lambda-fns/hello-world/`, { 14 | bundling: { 15 | image: Runtime.GO_1_X.bundlingImage, 16 | user: 'root', 17 | command: [ 18 | 'bash', '-c', [ 19 | 'cd /asset-input', 20 | 'go build -o main main.go', 21 | 'mv /asset-input/main /asset-output/' 22 | ].join(' && ') 23 | ] 24 | } 25 | }) 26 | }) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /go-lambda/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "go-lambda", 3 | "version": "1.1.0", 4 | "bin": { 5 | "go-lambda": "bin/go-lambda.js" 6 | }, 7 | "scripts": { 8 | "build": "tsc", 9 | "watch": "tsc -w", 10 | "test": "jest", 11 | "cdk": "cdk" 12 | }, 13 | "devDependencies": { 14 | "@types/jest": "^27.5.0", 15 | "@types/node": "10.17.27", 16 | "jest": "^27.5.1", 17 | "ts-jest": "^27.1.4", 18 | "aws-cdk": "2.38.1", 19 | "ts-node": "^10.7.0", 20 | "typescript": "~3.9.7" 21 | }, 22 | "dependencies": { 23 | "aws-cdk-lib": "2.38.1", 24 | "constructs": "^10.0.0", 25 | "source-map-support": "^0.5.21" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /go-lambda/test/go-lambda.test.ts: -------------------------------------------------------------------------------- 1 | import { expect as expectCDK, matchTemplate, MatchStyle } from '@aws-cdk/assert'; 2 | import * as cdk from '@aws-cdk/core'; 3 | import * as GoLambda from '../lib/go-lambda-stack'; 4 | 5 | test('Empty Stack', () => { 6 | const app = new cdk.App(); 7 | // WHEN 8 | const stack = new GoLambda.GoLambdaStack(app, 'MyTestStack'); 9 | // THEN 10 | expectCDK(stack).to(matchTemplate({ 11 | "Resources": {} 12 | }, MatchStyle.EXACT)) 13 | }); 14 | -------------------------------------------------------------------------------- /go-lambda/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2018", 4 | "module": "commonjs", 5 | "lib": ["es2018"], 6 | "declaration": true, 7 | "strict": true, 8 | "noImplicitAny": true, 9 | "strictNullChecks": true, 10 | "noImplicitThis": true, 11 | "alwaysStrict": true, 12 | "noUnusedLocals": false, 13 | "noUnusedParameters": false, 14 | "noImplicitReturns": true, 15 | "noFallthroughCasesInSwitch": false, 16 | "inlineSourceMap": true, 17 | "inlineSources": true, 18 | "experimentalDecorators": true, 19 | "strictPropertyInitialization": false, 20 | "typeRoots": ["./node_modules/@types"] 21 | }, 22 | "exclude": ["cdk.out"] 23 | } 24 | -------------------------------------------------------------------------------- /http-api/.gitignore: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /http-api/.npmignore: -------------------------------------------------------------------------------- 1 | *.ts 2 | !*.d.ts 3 | 4 | # CDK asset staging directory 5 | .cdk.staging 6 | cdk.out 7 | -------------------------------------------------------------------------------- /http-api/README.md: -------------------------------------------------------------------------------- 1 | # [HTTP API](https://apoorv.blog/http-api-cloudfront-cdk/) 2 | 3 | [![Read Tutorial](https://badgen.now.sh/badge/Read/Tutorial/purple)](https://apoorv.blog/http-api-cloudfront-cdk/) 4 | [![MIT License](https://badgen.now.sh/badge/License/MIT/blue)](https://github.com/apoorvmote/cdk-examples/blob/master/License.md) 5 | ![AWS CDK 2.38.1](https://badgen.net/badge/aws-cdk/2.38.1/yellow) 6 | ![Typescript](https://badgen.net/badge/icon/typescript?icon=typescript&label) 7 | 8 | We will deploy HTTP API with CDK and you can consume api via subdomain or via main domain. -------------------------------------------------------------------------------- /http-api/Taskfile.yml: -------------------------------------------------------------------------------- 1 | # https://taskfile.dev 2 | 3 | version: '3' 4 | 5 | tasks: 6 | update: 7 | cmds: 8 | - rm -rf node_modules/ 9 | - npm i 10 | fix: 11 | cmds: 12 | - npm audit fix -------------------------------------------------------------------------------- /http-api/bin/http-api.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import 'source-map-support/register'; 3 | import * as cdk from 'aws-cdk-lib'; 4 | import { HttpApiStack } from '../lib/http-api-stack'; 5 | import { virginia } from '../lib/variables'; 6 | 7 | const app = new cdk.App(); 8 | new HttpApiStack(app, 'HttpApiStack-mKsnyabf2uaVWcMR', { env: virginia }); 9 | -------------------------------------------------------------------------------- /http-api/cdk.json: -------------------------------------------------------------------------------- 1 | { 2 | "app": "npx ts-node --prefer-ts-exts bin/http-api.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-apigateway:usagePlanKeyOrderInsensitiveId": true, 21 | "@aws-cdk/core:stackRelativeExports": true, 22 | "@aws-cdk/aws-rds:lowercaseDbIdentifier": true, 23 | "@aws-cdk/aws-lambda:recognizeVersionProps": true, 24 | "@aws-cdk/aws-cloudfront:defaultSecurityPolicyTLSv1.2_2021": true 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /http-api/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | roots: ['/test'], 3 | testMatch: ['**/*.test.ts'], 4 | transform: { 5 | '^.+\\.tsx?$': 'ts-jest' 6 | } 7 | }; 8 | -------------------------------------------------------------------------------- /http-api/lambda-fns/sign-up/Taskfile.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | tasks: 4 | default: 5 | cmds: 6 | - rm -f deployment.zip 7 | - zip deployment.zip index.js -------------------------------------------------------------------------------- /http-api/lambda-fns/sign-up/deployment.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apoorvmote/cdk-examples/4fb551420a2da0ed2e186a133ebf2bb2fb54c38d/http-api/lambda-fns/sign-up/deployment.zip -------------------------------------------------------------------------------- /http-api/lambda-fns/sign-up/index.js: -------------------------------------------------------------------------------- 1 | exports.handler = async (event) => { 2 | 3 | const { username, email } = JSON.parse(event.body) 4 | 5 | return { 6 | statusCode: 200, 7 | body: JSON.stringify({ message: 'sign up success'}) 8 | } 9 | } -------------------------------------------------------------------------------- /http-api/lib/cloudfront-http-api-stack.ts: -------------------------------------------------------------------------------- 1 | import { Stack, StackProps, CfnOutput } from "aws-cdk-lib" 2 | import { Construct } from "constructs" 3 | import { DnsValidatedCertificate } from 'aws-cdk-lib/aws-certificatemanager'; 4 | import { HttpApi, HttpMethod } from '@aws-cdk/aws-apigatewayv2-alpha'; 5 | import { HttpLambdaIntegration } from '@aws-cdk/aws-apigatewayv2-integrations-alpha'; 6 | import { Architecture, Code, Function, Runtime } from 'aws-cdk-lib/aws-lambda'; 7 | import { ARecord, HostedZone, RecordTarget } from 'aws-cdk-lib/aws-route53'; 8 | import { CloudFrontTarget } from 'aws-cdk-lib/aws-route53-targets'; 9 | import { hostedZoneId, website_domain } from './variables'; 10 | import { HttpOrigin } from 'aws-cdk-lib/aws-cloudfront-origins'; 11 | import { AllowedMethods, CachePolicy, Distribution, HttpVersion, OriginRequestCookieBehavior, OriginRequestHeaderBehavior, OriginRequestPolicy, OriginRequestQueryStringBehavior, PriceClass, SecurityPolicyProtocol, ViewerProtocolPolicy } from 'aws-cdk-lib/aws-cloudfront'; 12 | 13 | 14 | export class CloudfrontHttpApiStack extends Stack { 15 | constructor(scope: Construct, id: string, props?: StackProps) { 16 | super(scope, id, props); 17 | 18 | // The code that defines your stack goes here 19 | /////////////////////////////// 20 | // Part 1 21 | const hostedZone = HostedZone.fromHostedZoneAttributes(this, 'hostedZoneWithAttributes', { 22 | hostedZoneId, 23 | zoneName: website_domain 24 | }) 25 | 26 | const certificate = new DnsValidatedCertificate(this, 'ApiSSL', { 27 | domainName: website_domain, 28 | hostedZone 29 | }) 30 | 31 | const api = new HttpApi(this, 'apiEndpoint', { 32 | apiName: 'exampleAPISameDomain', 33 | }) 34 | 35 | new CfnOutput(this, 'apiID', { 36 | value: api.apiEndpoint 37 | }) 38 | 39 | const signUpFn = new Function(this, 'signUpFn', { 40 | runtime: Runtime.NODEJS_16_X, 41 | code: Code.fromAsset(`${__dirname}/../lambda-fns/sign-up/deployment.zip`), 42 | handler: 'index.handler', 43 | memorySize: 512, 44 | architecture: Architecture.ARM_64 45 | }) 46 | /////////////////////////////// 47 | 48 | /////////////////////////////// 49 | // Part 2 50 | api.addRoutes({ 51 | path: '/api/sign-up', 52 | methods: [HttpMethod.POST], 53 | integration: new HttpLambdaIntegration('signUpFn', signUpFn) 54 | }) 55 | 56 | const apiOriginPolicy = new OriginRequestPolicy(this, 'apiOriginPolicy', { 57 | cookieBehavior: OriginRequestCookieBehavior.all(), 58 | headerBehavior: OriginRequestHeaderBehavior.none(), 59 | queryStringBehavior: OriginRequestQueryStringBehavior.all() 60 | }) 61 | 62 | const distribution = new Distribution(this, 'websiteAndAPIDistribution', { 63 | defaultBehavior: { 64 | origin: new HttpOrigin('origin-source-code.com'), 65 | }, 66 | additionalBehaviors: { 67 | 'api/*': { 68 | origin: new HttpOrigin(api.apiEndpoint.replace('https://', '')), 69 | allowedMethods: AllowedMethods.ALLOW_ALL, 70 | cachePolicy: CachePolicy.CACHING_DISABLED, 71 | compress: false, 72 | originRequestPolicy: apiOriginPolicy, 73 | viewerProtocolPolicy: ViewerProtocolPolicy.REDIRECT_TO_HTTPS 74 | } 75 | }, 76 | domainNames: [website_domain], 77 | certificate, 78 | minimumProtocolVersion: SecurityPolicyProtocol.TLS_V1_2_2021, 79 | enableIpv6: true, 80 | enabled: true, 81 | httpVersion: HttpVersion.HTTP2, 82 | priceClass: PriceClass.PRICE_CLASS_ALL 83 | }) 84 | /////////////////////////////// 85 | 86 | /////////////////////////////// 87 | // Part 3 88 | new ARecord(this, 'AliasForCloudfront', { 89 | target: RecordTarget.fromAlias(new CloudFrontTarget(distribution)), 90 | zone: hostedZone, 91 | recordName: website_domain 92 | }) 93 | /////////////////////////////// 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /http-api/lib/http-api-stack.ts: -------------------------------------------------------------------------------- 1 | import { Stack, StackProps, CfnOutput } from "aws-cdk-lib" 2 | import { Construct } from "constructs" 3 | import { CorsHttpMethod, DomainName, HttpApi, HttpMethod } from '@aws-cdk/aws-apigatewayv2-alpha'; 4 | import { HttpLambdaIntegration } from '@aws-cdk/aws-apigatewayv2-integrations-alpha'; 5 | import { DnsValidatedCertificate } from 'aws-cdk-lib/aws-certificatemanager'; 6 | import { Architecture, Code, Function, Runtime } from 'aws-cdk-lib/aws-lambda'; 7 | import { ARecord, HostedZone, RecordTarget } from 'aws-cdk-lib/aws-route53'; 8 | import { ApiGatewayv2DomainProperties } from 'aws-cdk-lib/aws-route53-targets'; 9 | import { api_domain, hostedZoneId, website_domain } from './variables'; 10 | 11 | export class HttpApiStack extends Stack { 12 | constructor(scope: Construct, id: string, props?: StackProps) { 13 | super(scope, id, props); 14 | 15 | // The code that defines your stack goes here 16 | 17 | /////////////////////////////// 18 | // Part 1 19 | const hostedZone = HostedZone.fromHostedZoneAttributes(this, 'hostedZoneWithAttributes', { 20 | hostedZoneId, 21 | zoneName: website_domain 22 | }) 23 | 24 | const apiCert = new DnsValidatedCertificate(this, 'ApiSSL', { 25 | domainName: api_domain, 26 | hostedZone 27 | }) 28 | /////////////////////////////// 29 | 30 | /////////////////////////////// 31 | // Part 2 32 | const domain = new DomainName(this, 'api_domain', { 33 | domainName: api_domain, 34 | certificate: apiCert 35 | }) 36 | 37 | const api = new HttpApi(this, 'apiEndpoint', { 38 | defaultDomainMapping: { 39 | domainName: domain 40 | }, 41 | corsPreflight: { 42 | allowCredentials: true, 43 | allowHeaders: ['Content-Type'], 44 | allowMethods: [CorsHttpMethod.GET, CorsHttpMethod.POST, CorsHttpMethod.PUT, CorsHttpMethod.DELETE], 45 | allowOrigins: [ 46 | 'https://example.com' 47 | ] 48 | }, 49 | apiName: 'exampleAPI', 50 | disableExecuteApiEndpoint: true 51 | }) 52 | 53 | new CfnOutput(this, 'apiID', { 54 | value: api.httpApiId 55 | }) 56 | 57 | const signUpFn = new Function(this, 'signUpFn', { 58 | runtime: Runtime.NODEJS_16_X, 59 | code: Code.fromAsset(`${__dirname}/../lambda-fns/sign-up/deployment.zip`), 60 | handler: 'index.handler', 61 | architecture: Architecture.ARM_64, 62 | memorySize: 512 63 | }) 64 | /////////////////////////////// 65 | 66 | // const existingAPI = HttpApi.fromHttpApiAttributes(this, 'existingAPI', { 67 | // apiEndpoint: api_domain, 68 | // httpApiId: 'myApiId' 69 | // }) 70 | 71 | /////////////////////////////// 72 | // Part 3 73 | new ARecord(this, 'apiAliasRecord', { 74 | zone: hostedZone, 75 | target: RecordTarget.fromAlias(new ApiGatewayv2DomainProperties(domain.regionalDomainName, domain.regionalHostedZoneId)) 76 | }) 77 | 78 | api.addRoutes({ 79 | path: '/sign-up', 80 | methods: [HttpMethod.POST], 81 | integration: new HttpLambdaIntegration('signUpFn', signUpFn) 82 | }) 83 | /////////////////////////////// 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /http-api/lib/variables.ts: -------------------------------------------------------------------------------- 1 | export const virginia = { account: '1234567890', region: 'us-east-1' } 2 | 3 | export const hostedZoneId = 'myZoneId' 4 | 5 | export const website_domain = 'example.com' 6 | 7 | export const api_domain = 'api.example.com' -------------------------------------------------------------------------------- /http-api/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "http-api", 3 | "version": "1.1.0", 4 | "bin": { 5 | "http-api": "bin/http-api.js" 6 | }, 7 | "scripts": { 8 | "build": "tsc", 9 | "watch": "tsc -w", 10 | "test": "jest", 11 | "cdk": "cdk" 12 | }, 13 | "devDependencies": { 14 | "@types/jest": "^27.5.0", 15 | "@types/node": "10.17.27", 16 | "jest": "^27.5.1", 17 | "ts-jest": "^27.1.4", 18 | "aws-cdk": "2.38.1", 19 | "ts-node": "^10.7.0", 20 | "typescript": "~3.9.7" 21 | }, 22 | "dependencies": { 23 | "@aws-cdk/aws-apigatewayv2-alpha": "^2.38.1-alpha.0", 24 | "@aws-cdk/aws-apigatewayv2-integrations-alpha": "^2.38.1-alpha.0", 25 | "aws-cdk-lib": "2.38.1", 26 | "constructs": "^10.0.0", 27 | "source-map-support": "^0.5.21" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /http-api/test/http-api.test.ts: -------------------------------------------------------------------------------- 1 | import { expect as expectCDK, matchTemplate, MatchStyle } from '@aws-cdk/assert'; 2 | import * as cdk from '@aws-cdk/core'; 3 | import * as HttpApi from '../lib/http-api-stack'; 4 | 5 | test('Empty Stack', () => { 6 | const app = new cdk.App(); 7 | // WHEN 8 | const stack = new HttpApi.HttpApiStack(app, 'MyTestStack'); 9 | // THEN 10 | expectCDK(stack).to(matchTemplate({ 11 | "Resources": {} 12 | }, MatchStyle.EXACT)) 13 | }); 14 | -------------------------------------------------------------------------------- /http-api/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2018", 4 | "module": "commonjs", 5 | "lib": ["es2018"], 6 | "declaration": true, 7 | "strict": true, 8 | "noImplicitAny": true, 9 | "strictNullChecks": true, 10 | "noImplicitThis": true, 11 | "alwaysStrict": true, 12 | "noUnusedLocals": false, 13 | "noUnusedParameters": false, 14 | "noImplicitReturns": true, 15 | "noFallthroughCasesInSwitch": false, 16 | "inlineSourceMap": true, 17 | "inlineSources": true, 18 | "experimentalDecorators": true, 19 | "strictPropertyInitialization": false, 20 | "typeRoots": ["./node_modules/@types"] 21 | }, 22 | "exclude": ["cdk.out"] 23 | } 24 | -------------------------------------------------------------------------------- /https-redirect/.gitignore: -------------------------------------------------------------------------------- 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 | 10 | # Parcel default cache directory 11 | .parcel-cache 12 | -------------------------------------------------------------------------------- /https-redirect/.npmignore: -------------------------------------------------------------------------------- 1 | *.ts 2 | !*.d.ts 3 | 4 | # CDK asset staging directory 5 | .cdk.staging 6 | cdk.out 7 | -------------------------------------------------------------------------------- /https-redirect/README.md: -------------------------------------------------------------------------------- 1 | # [https redirect / www to non-www redirect](https://apoorv.blog/redirect-www-to-non-www/) 2 | 3 | [![Read Tutorial](https://badgen.now.sh/badge/Read/Tutorial/purple)](https://apoorv.blog/redirect-www-to-non-www/) 4 | [![MIT License](https://badgen.now.sh/badge/License/MIT/blue)](https://github.com/apoorvmote/cdk-examples/blob/master/License.md) 5 | ![AWS CDK 2.38.1](https://badgen.net/badge/aws-cdk/2.38.1/yellow) 6 | ![Typescript](https://badgen.net/badge/icon/typescript?icon=typescript&label) 7 | 8 | This is designed for any https redirect but I primarily use it for www to non-www redirect -------------------------------------------------------------------------------- /https-redirect/Taskfile.yml: -------------------------------------------------------------------------------- 1 | # https://taskfile.dev 2 | 3 | version: '3' 4 | 5 | tasks: 6 | update: 7 | cmds: 8 | - rm -rf node_modules/ 9 | - npm i 10 | fix: 11 | cmds: 12 | - npm audit fix -------------------------------------------------------------------------------- /https-redirect/bin/https-redirect.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import 'source-map-support/register'; 3 | import * as cdk from 'aws-cdk-lib'; 4 | import { HttpsRedirectStack } from '../lib/https-redirect-stack'; 5 | 6 | const app = new cdk.App(); 7 | 8 | const virginia = { account: '012345678901', region: 'us-east-1' } 9 | 10 | new HttpsRedirectStack(app, 'HttpsRedirectStack', { env: virginia }); -------------------------------------------------------------------------------- /https-redirect/cdk.json: -------------------------------------------------------------------------------- 1 | { 2 | "app": "npx ts-node --prefer-ts-exts bin/https-redirect.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-apigateway:usagePlanKeyOrderInsensitiveId": true, 21 | "@aws-cdk/core:stackRelativeExports": true, 22 | "@aws-cdk/aws-rds:lowercaseDbIdentifier": true, 23 | "@aws-cdk/aws-lambda:recognizeVersionProps": true, 24 | "@aws-cdk/aws-cloudfront:defaultSecurityPolicyTLSv1.2_2021": true 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /https-redirect/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | roots: ['/test'], 3 | testMatch: ['**/*.test.ts'], 4 | transform: { 5 | '^.+\\.tsx?$': 'ts-jest' 6 | } 7 | }; 8 | -------------------------------------------------------------------------------- /https-redirect/lib/https-redirect-stack.ts: -------------------------------------------------------------------------------- 1 | import { Stack, StackProps } from "aws-cdk-lib" 2 | import { Construct } from "constructs" 3 | import { HostedZone } from 'aws-cdk-lib/aws-route53'; 4 | import { HttpsRedirect } from 'aws-cdk-lib/aws-route53-patterns'; 5 | 6 | export class HttpsRedirectStack extends Stack { 7 | constructor(scope: Construct, id: string, props?: StackProps) { 8 | super(scope, id, props); 9 | 10 | // The code that defines your stack goes here 11 | 12 | const hostedZone = HostedZone.fromHostedZoneAttributes(this, 'HostedZoneWithAttrs', { 13 | hostedZoneId: 'myZoneId', 14 | zoneName: 'example.com' 15 | }) 16 | 17 | new HttpsRedirect(this, 'wwwToNonWww', { 18 | recordNames: ['www.example.com'], 19 | targetDomain: 'example.com', 20 | zone: hostedZone 21 | }) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /https-redirect/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "https-redirect", 3 | "version": "1.1.0", 4 | "bin": { 5 | "https-redirect": "bin/https-redirect.js" 6 | }, 7 | "scripts": { 8 | "build": "tsc", 9 | "watch": "tsc -w", 10 | "test": "jest", 11 | "cdk": "cdk" 12 | }, 13 | "devDependencies": { 14 | "@types/jest": "^27.5.0", 15 | "@types/node": "10.17.27", 16 | "jest": "^27.5.1", 17 | "ts-jest": "^27.1.4", 18 | "aws-cdk": "2.38.1", 19 | "ts-node": "^10.7.0", 20 | "typescript": "~3.9.7" 21 | }, 22 | "dependencies": { 23 | "aws-cdk-lib": "2.38.1", 24 | "constructs": "^10.0.0", 25 | "source-map-support": "^0.5.21" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /https-redirect/test/https-redirect.test.ts: -------------------------------------------------------------------------------- 1 | import { expect as expectCDK, matchTemplate, MatchStyle } from '@aws-cdk/assert'; 2 | import * as cdk from '@aws-cdk/core'; 3 | import * as HttpsRedirect from '../lib/https-redirect-stack'; 4 | 5 | test('Empty Stack', () => { 6 | const app = new cdk.App(); 7 | // WHEN 8 | const stack = new HttpsRedirect.HttpsRedirectStack(app, 'MyTestStack'); 9 | // THEN 10 | expectCDK(stack).to(matchTemplate({ 11 | "Resources": {} 12 | }, MatchStyle.EXACT)) 13 | }); 14 | -------------------------------------------------------------------------------- /https-redirect/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2018", 4 | "module": "commonjs", 5 | "lib": ["es2018"], 6 | "declaration": true, 7 | "strict": true, 8 | "noImplicitAny": true, 9 | "strictNullChecks": true, 10 | "noImplicitThis": true, 11 | "alwaysStrict": true, 12 | "noUnusedLocals": false, 13 | "noUnusedParameters": false, 14 | "noImplicitReturns": true, 15 | "noFallthroughCasesInSwitch": false, 16 | "inlineSourceMap": true, 17 | "inlineSources": true, 18 | "experimentalDecorators": true, 19 | "strictPropertyInitialization": false, 20 | "typeRoots": ["./node_modules/@types"] 21 | }, 22 | "exclude": ["cdk.out"] 23 | } 24 | -------------------------------------------------------------------------------- /hugo-pipeline/Readme.md: -------------------------------------------------------------------------------- 1 | # [Build and deploy Hugo website with Code Pipeline and Cloudfront](https://apoorv.blog/deploy-hugo-cloudfront-codepipeline-cdk/) 2 | 3 | [![Read Tutorial](https://badgen.now.sh/badge/Read/Tutorial/purple)](https://apoorv.blog/deploy-hugo-cloudfront-codepipeline-cdk/) 4 | [![MIT License](https://badgen.now.sh/badge/License/MIT/blue)](https://github.com/apoorvmote/cdk-examples/blob/master/License.md) 5 | ![AWS CDK 2.38.1](https://badgen.net/badge/aws-cdk/2.38.1/yellow) 6 | ![Typescript](https://badgen.net/badge/icon/typescript?icon=typescript&label) 7 | 8 | **Very important** make sure to deploy stacks in Parts as assigned in the code. 9 | 10 | In this example Hugo website is stored in AWS CodeCommit, built with AWS Codepipeline and deployed to AWS S3 and AWS Cloudfront -------------------------------------------------------------------------------- /hugo-pipeline/backend-infra/.gitignore: -------------------------------------------------------------------------------- 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 | 10 | # Parcel default cache directory 11 | .parcel-cache 12 | -------------------------------------------------------------------------------- /hugo-pipeline/backend-infra/.npmignore: -------------------------------------------------------------------------------- 1 | *.ts 2 | !*.d.ts 3 | 4 | # CDK asset staging directory 5 | .cdk.staging 6 | cdk.out 7 | -------------------------------------------------------------------------------- /hugo-pipeline/backend-infra/README.md: -------------------------------------------------------------------------------- 1 | # Welcome to your CDK TypeScript project! 2 | 3 | This is a blank project for TypeScript development with CDK. 4 | 5 | The `cdk.json` file tells the CDK Toolkit how to execute your app. 6 | 7 | ## Useful commands 8 | 9 | * `npm run build` compile typescript to js 10 | * `npm run watch` watch for changes and compile 11 | * `npm run test` perform the jest unit tests 12 | * `cdk deploy` deploy this stack to your default AWS account/region 13 | * `cdk diff` compare deployed stack with current state 14 | * `cdk synth` emits the synthesized CloudFormation template 15 | -------------------------------------------------------------------------------- /hugo-pipeline/backend-infra/Taskfile.yml: -------------------------------------------------------------------------------- 1 | # https://taskfile.dev 2 | 3 | version: '3' 4 | 5 | tasks: 6 | update: 7 | cmds: 8 | - rm -rf node_modules/ 9 | - npm i 10 | fix: 11 | cmds: 12 | - npm audit fix -------------------------------------------------------------------------------- /hugo-pipeline/backend-infra/bin/backend-infra.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import 'source-map-support/register'; 3 | import * as cdk from 'aws-cdk-lib'; 4 | import { CertificateStack } from '../lib/certificate'; 5 | import { S3CloudfrontStack } from '../lib/s3cloudfront'; 6 | import { CodePipelineStack } from '../lib/code-pipeline'; 7 | import { virginia } from '../lib/variables'; 8 | // import { BackendInfraStack } from '../lib/backend-infra-stack'; 9 | 10 | const app = new cdk.App(); 11 | // new BackendInfraStack(app, 'BackendInfraStack'); 12 | 13 | new CertificateStack(app, 'certificate-UfyGK3cks5nZs3Wc', { env: virginia }) 14 | 15 | new S3CloudfrontStack(app, 's3cloudfront-3ZFF5p53aXZaPqWB', { env: virginia }) 16 | 17 | new CodePipelineStack(app, 'codepipeline-22RY5f7eRyfWfDjb', { env: virginia }) -------------------------------------------------------------------------------- /hugo-pipeline/backend-infra/cdk.json: -------------------------------------------------------------------------------- 1 | { 2 | "app": "npx ts-node --prefer-ts-exts bin/backend-infra.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-apigateway:usagePlanKeyOrderInsensitiveId": true, 21 | "@aws-cdk/core:stackRelativeExports": true, 22 | "@aws-cdk/aws-rds:lowercaseDbIdentifier": true, 23 | "@aws-cdk/aws-lambda:recognizeVersionProps": true, 24 | "@aws-cdk/aws-cloudfront:defaultSecurityPolicyTLSv1.2_2021": true 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /hugo-pipeline/backend-infra/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | roots: ['/test'], 3 | testMatch: ['**/*.test.ts'], 4 | transform: { 5 | '^.+\\.tsx?$': 'ts-jest' 6 | } 7 | }; 8 | -------------------------------------------------------------------------------- /hugo-pipeline/backend-infra/lib/backend-infra-stack.ts: -------------------------------------------------------------------------------- 1 | import { Stack, StackProps } from "aws-cdk-lib" 2 | import { Construct } from "constructs" 3 | 4 | export class BackendInfraStack extends Stack { 5 | constructor(scope: Construct, id: string, props?: StackProps) { 6 | super(scope, id, props); 7 | 8 | // The code that defines your stack goes here 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /hugo-pipeline/backend-infra/lib/certificate.ts: -------------------------------------------------------------------------------- 1 | import { Stack, StackProps, CfnOutput } from "aws-cdk-lib" 2 | import { Construct } from "constructs" 3 | import { DnsValidatedCertificate } from 'aws-cdk-lib/aws-certificatemanager'; 4 | import { HostedZone } from 'aws-cdk-lib/aws-route53'; 5 | import { hostedZoneId, website_domain } from './variables'; 6 | 7 | export class CertificateStack extends Stack { 8 | constructor(scope: Construct, id: string, props?: StackProps) { 9 | super(scope, id, props); 10 | /////////////////////////////// 11 | // Part 1 12 | const hostedZone = HostedZone.fromHostedZoneAttributes(this, 'HostedZoneWithAttrs', { 13 | hostedZoneId, 14 | zoneName: website_domain 15 | }) 16 | 17 | const websiteCert = new DnsValidatedCertificate(this, 'WebsiteSSL', { 18 | domainName: website_domain, 19 | hostedZone 20 | }) 21 | 22 | new CfnOutput(this, 'WebsiteCertArn', { 23 | value: websiteCert.certificateArn 24 | }) 25 | /////////////////////////////// 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /hugo-pipeline/backend-infra/lib/code-pipeline.ts: -------------------------------------------------------------------------------- 1 | import { Stack, StackProps, RemovalPolicy } from "aws-cdk-lib" 2 | import { Construct } from "constructs" 3 | import { BuildSpec, ComputeType, LinuxBuildImage, PipelineProject } from 'aws-cdk-lib/aws-codebuild'; 4 | import { Repository } from 'aws-cdk-lib/aws-codecommit'; 5 | import { Artifact, Pipeline } from 'aws-cdk-lib/aws-codepipeline'; 6 | import { CodeBuildAction, CodeCommitSourceAction, S3DeployAction } from 'aws-cdk-lib/aws-codepipeline-actions'; 7 | import { Bucket } from 'aws-cdk-lib/aws-s3'; 8 | import { hugoRepoArn, websiteBucketArn } from './variables'; 9 | 10 | export class CodePipelineStack extends Stack { 11 | constructor(scope: Construct, id: string, props?: StackProps) { 12 | super(scope, id, props); 13 | 14 | /////////////////////////////// 15 | // Part 7 16 | const websiteBucket = Bucket.fromBucketArn(this, 'websiteBucket', websiteBucketArn) 17 | 18 | const hugoBuildProject = new PipelineProject(this, 'hugoBuild', { 19 | buildSpec: BuildSpec.fromSourceFilename('buildspec.yml'), 20 | environment: { 21 | buildImage: LinuxBuildImage.STANDARD_5_0, 22 | computeType: ComputeType.SMALL 23 | } 24 | }) 25 | 26 | const artifactBucket = new Bucket(this, 'websitePipelineArtifactBucket', { 27 | bucketName: 'hugo-pipeline-artifact-bucket', 28 | removalPolicy: RemovalPolicy.DESTROY 29 | }) 30 | 31 | const gitOutput = new Artifact('hugoRepoLatestMaster') 32 | 33 | const buildOutput = new Artifact('hugoBuildOutput') 34 | 35 | new Pipeline(this, 'hugoPipeline', { 36 | artifactBucket, 37 | pipelineName: 'examplePipeline', 38 | stages: [ 39 | { 40 | stageName: 'SourceCode', 41 | actions: [ 42 | new CodeCommitSourceAction({ 43 | actionName: 'readLatestMasterCommit', 44 | branch: 'main', 45 | output: gitOutput, 46 | repository: Repository.fromRepositoryArn(this, 'hugoGitRepo', hugoRepoArn) 47 | }) 48 | ] 49 | }, 50 | { 51 | stageName: 'Build', 52 | actions: [ 53 | new CodeBuildAction({ 54 | actionName: 'buildHugoWebsite', 55 | input: gitOutput, 56 | outputs: [buildOutput], 57 | project: hugoBuildProject 58 | }) 59 | ] 60 | }, 61 | { 62 | stageName: 'Deploy', 63 | actions: [ 64 | new S3DeployAction({ 65 | actionName: 'DeployHugoWebsite', 66 | input: buildOutput, 67 | bucket: websiteBucket 68 | }) 69 | ] 70 | } 71 | ] 72 | }) 73 | /////////////////////////////// 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /hugo-pipeline/backend-infra/lib/s3cloudfront.ts: -------------------------------------------------------------------------------- 1 | import { Stack, StackProps, CfnOutput, RemovalPolicy, Duration } from "aws-cdk-lib" 2 | import { Construct } from "constructs" 3 | import { Certificate } from 'aws-cdk-lib/aws-certificatemanager'; 4 | import { AllowedMethods, CachePolicy, CloudFrontWebDistribution, Distribution, HttpVersion, OriginAccessIdentity, OriginProtocolPolicy, PriceClass, SecurityPolicyProtocol, ViewerCertificate, ViewerProtocolPolicy } from 'aws-cdk-lib/aws-cloudfront'; 5 | import { HttpOrigin } from 'aws-cdk-lib/aws-cloudfront-origins'; 6 | import { Repository } from 'aws-cdk-lib/aws-codecommit'; 7 | import { AnyPrincipal, Effect, PolicyStatement } from 'aws-cdk-lib/aws-iam'; 8 | import { ARecord, HostedZone, RecordTarget } from 'aws-cdk-lib/aws-route53'; 9 | import { HttpsRedirect } from 'aws-cdk-lib/aws-route53-patterns'; 10 | import { CloudFrontTarget } from 'aws-cdk-lib/aws-route53-targets'; 11 | import { Bucket } from 'aws-cdk-lib/aws-s3'; 12 | import { bucketRefererSecret, bucketWebsiteUrl, hostedZoneId, websiteCertArn, website_domain } from './variables'; 13 | 14 | export class S3CloudfrontStack extends Stack { 15 | constructor(scope: Construct, id: string, props?: StackProps) { 16 | super(scope, id, props); 17 | 18 | /////////////////////////////// 19 | // Part 2 20 | const bucket = new Bucket(this, 'websiteBucket', { 21 | removalPolicy: RemovalPolicy.DESTROY, 22 | bucketName: website_domain, 23 | autoDeleteObjects: true, 24 | websiteIndexDocument: 'index.html', 25 | websiteErrorDocument: '404.html' 26 | }) 27 | 28 | bucket.addToResourcePolicy(new PolicyStatement({ 29 | effect: Effect.ALLOW, 30 | principals: [new AnyPrincipal()], 31 | actions: ['s3:GetObject'], 32 | resources: ['arn:aws:s3:::example.com/*'], 33 | conditions: { 34 | "StringLike":{"aws:Referer":[bucketRefererSecret]} 35 | } 36 | })) 37 | 38 | new CfnOutput(this, 'websiteBucketArn', { 39 | value: bucket.bucketArn 40 | }) 41 | 42 | new CfnOutput(this, 'websiteBucketUrl', { 43 | value: bucket.bucketWebsiteUrl 44 | }) 45 | /////////////////////////////// 46 | 47 | /////////////////////////////// 48 | // Part 3 49 | const certificate = Certificate.fromCertificateArn(this, 'websiteCert', websiteCertArn) 50 | 51 | const cachePolicy = new CachePolicy(this, 'examplePolicy', { 52 | defaultTtl: Duration.hours(24), 53 | minTtl: Duration.hours(24), 54 | maxTtl: Duration.hours(24), 55 | enableAcceptEncodingBrotli: true, 56 | enableAcceptEncodingGzip: true 57 | }) 58 | 59 | const distribution = new Distribution(this, 'exampleDistribution', { 60 | defaultBehavior: { 61 | origin: new HttpOrigin(bucketWebsiteUrl, { 62 | protocolPolicy: OriginProtocolPolicy.HTTP_ONLY, 63 | customHeaders: { 64 | 'Referer': bucketRefererSecret 65 | } 66 | }), 67 | allowedMethods: AllowedMethods.ALLOW_GET_HEAD_OPTIONS, 68 | cachePolicy, 69 | compress: true, 70 | viewerProtocolPolicy: ViewerProtocolPolicy.REDIRECT_TO_HTTPS 71 | }, 72 | domainNames: [website_domain], 73 | certificate, 74 | minimumProtocolVersion: SecurityPolicyProtocol.TLS_V1_2_2021, 75 | enableIpv6: true, 76 | enabled: true, 77 | httpVersion: HttpVersion.HTTP2, 78 | priceClass: PriceClass.PRICE_CLASS_ALL 79 | }) 80 | /////////////////////////////// 81 | 82 | /////////////////////////////// 83 | // Part 4 84 | const hostedZone = HostedZone.fromHostedZoneAttributes(this, 'hostedZoneWithAttrs', { 85 | hostedZoneId, 86 | zoneName: website_domain 87 | }) 88 | 89 | new ARecord(this, 'aliasForCloudfront', { 90 | target: RecordTarget.fromAlias(new CloudFrontTarget(distribution)), 91 | zone: hostedZone, 92 | recordName: website_domain 93 | }) 94 | /////////////////////////////// 95 | 96 | /////////////////////////////// 97 | // Part 5 98 | new HttpsRedirect(this, 'wwwToNonWww', { 99 | recordNames: ['www.example.com'], 100 | targetDomain: website_domain, 101 | zone:hostedZone 102 | }) 103 | 104 | const repo = new Repository(this, 'hugoSourceCode', { 105 | repositoryName: 'example', 106 | description: `hugo repo for ${website_domain}` 107 | }) 108 | 109 | new CfnOutput(this, 'hugoRepoArn', { 110 | value: repo.repositoryArn 111 | }) 112 | /////////////////////////////// 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /hugo-pipeline/backend-infra/lib/variables.ts: -------------------------------------------------------------------------------- 1 | export const hostedZoneId = 'myZoneId' 2 | 3 | export const website_domain = 'example.com' 4 | 5 | export const websiteCertArn = 'myCertificateArn' 6 | 7 | export const websiteBucketArn = 'arn:aws:s3:::example.com' 8 | 9 | export const hugoRepoArn = 'myRepoArn' 10 | 11 | export const virginia = { account: '012345678901', region: 'us-east-1' } 12 | 13 | export const bucketWebsiteUrl = 'example.com.s3-website-us-east-1.amazonaws.com' 14 | 15 | export const bucketRefererSecret = 'my-secret-key' -------------------------------------------------------------------------------- /hugo-pipeline/backend-infra/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "backend-infra", 3 | "version": "0.1.0", 4 | "bin": { 5 | "backend-infra": "bin/backend-infra.js" 6 | }, 7 | "scripts": { 8 | "build": "tsc", 9 | "watch": "tsc -w", 10 | "test": "jest", 11 | "cdk": "cdk" 12 | }, 13 | "devDependencies": { 14 | "@types/jest": "^27.5.0", 15 | "@types/node": "10.17.27", 16 | "jest": "^27.5.1", 17 | "ts-jest": "^27.1.4", 18 | "aws-cdk": "2.38.1", 19 | "ts-node": "^10.7.0", 20 | "typescript": "~3.9.7" 21 | }, 22 | "dependencies": { 23 | "aws-cdk-lib": "2.38.1", 24 | "constructs": "^10.0.0", 25 | "source-map-support": "^0.5.21" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /hugo-pipeline/backend-infra/test/backend-infra.test.ts: -------------------------------------------------------------------------------- 1 | import { expect as expectCDK, matchTemplate, MatchStyle } from '@aws-cdk/assert'; 2 | import * as cdk from '@aws-cdk/core'; 3 | import * as BackendInfra from '../lib/backend-infra-stack'; 4 | 5 | test('Empty Stack', () => { 6 | const app = new cdk.App(); 7 | // WHEN 8 | const stack = new BackendInfra.BackendInfraStack(app, 'MyTestStack'); 9 | // THEN 10 | expectCDK(stack).to(matchTemplate({ 11 | "Resources": {} 12 | }, MatchStyle.EXACT)) 13 | }); 14 | -------------------------------------------------------------------------------- /hugo-pipeline/backend-infra/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2018", 4 | "module": "commonjs", 5 | "lib": ["es2018"], 6 | "declaration": true, 7 | "strict": true, 8 | "noImplicitAny": true, 9 | "strictNullChecks": true, 10 | "noImplicitThis": true, 11 | "alwaysStrict": true, 12 | "noUnusedLocals": false, 13 | "noUnusedParameters": false, 14 | "noImplicitReturns": true, 15 | "noFallthroughCasesInSwitch": false, 16 | "inlineSourceMap": true, 17 | "inlineSources": true, 18 | "experimentalDecorators": true, 19 | "strictPropertyInitialization": false, 20 | "typeRoots": ["./node_modules/@types"] 21 | }, 22 | "exclude": ["cdk.out"] 23 | } 24 | -------------------------------------------------------------------------------- /hugo-pipeline/hugo-website/archetypes/default.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "{{ replace .Name "-" " " | title }}" 3 | date: {{ .Date }} 4 | draft: true 5 | --- 6 | 7 | -------------------------------------------------------------------------------- /hugo-pipeline/hugo-website/buildspec.yml: -------------------------------------------------------------------------------- 1 | version: 0.2 2 | 3 | env: 4 | variables: 5 | hugo_version: "0.80.0" 6 | 7 | phases: 8 | install: 9 | runtime-versions: 10 | nodejs: 14.x 11 | commands: 12 | - echo Installing hugo 13 | - wget "https://github.com/gohugoio/hugo/releases/download/v${hugo_version}/hugo_extended_${hugo_version}_Linux-64bit.deb" 14 | - dpkg -i hugo_extended_${hugo_version}_Linux-64bit.deb 15 | - echo Installing npm packages 16 | - npm install 17 | - npm update 18 | finally: 19 | - hugo version 20 | build: 21 | commands: 22 | - git config --global user.email "test@example.com" 23 | - git config --global user.name "John Doe" 24 | - git init 25 | - git add . 26 | - git commit -m 'build commit' 27 | - echo Building Website content 28 | - hugo 29 | - echo Built hugo on `date` 30 | 31 | artifacts: 32 | base-directory: ./public 33 | files: 34 | - '**/*' -------------------------------------------------------------------------------- /hugo-pipeline/hugo-website/config.toml: -------------------------------------------------------------------------------- 1 | baseURL = "http://example.org/" 2 | languageCode = "en-us" 3 | title = "My New Hugo Site" 4 | -------------------------------------------------------------------------------- /hugo-pipeline/hugo-website/content/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apoorvmote/cdk-examples/4fb551420a2da0ed2e186a133ebf2bb2fb54c38d/hugo-pipeline/hugo-website/content/.gitkeep -------------------------------------------------------------------------------- /hugo-pipeline/hugo-website/data/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apoorvmote/cdk-examples/4fb551420a2da0ed2e186a133ebf2bb2fb54c38d/hugo-pipeline/hugo-website/data/.gitkeep -------------------------------------------------------------------------------- /hugo-pipeline/hugo-website/layouts/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apoorvmote/cdk-examples/4fb551420a2da0ed2e186a133ebf2bb2fb54c38d/hugo-pipeline/hugo-website/layouts/.gitkeep -------------------------------------------------------------------------------- /hugo-pipeline/hugo-website/static/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apoorvmote/cdk-examples/4fb551420a2da0ed2e186a133ebf2bb2fb54c38d/hugo-pipeline/hugo-website/static/.gitkeep -------------------------------------------------------------------------------- /hugo-pipeline/hugo-website/themes/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apoorvmote/cdk-examples/4fb551420a2da0ed2e186a133ebf2bb2fb54c38d/hugo-pipeline/hugo-website/themes/.gitkeep -------------------------------------------------------------------------------- /lambda-cost/.gitignore: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /lambda-cost/.npmignore: -------------------------------------------------------------------------------- 1 | *.ts 2 | !*.d.ts 3 | 4 | # CDK asset staging directory 5 | .cdk.staging 6 | cdk.out 7 | -------------------------------------------------------------------------------- /lambda-cost/README.md: -------------------------------------------------------------------------------- 1 | # [Lambda Cost Optimization](https://apoorv.blog/optimize-lambda-cost/) 2 | 3 | [![Read Tutorial](https://badgen.now.sh/badge/Read/Tutorial/purple)](https://apoorv.blog/optimize-lambda-cost/) 4 | [![MIT License](https://badgen.now.sh/badge/License/MIT/blue)](https://github.com/apoorvmote/cdk-examples/blob/master/License.md) 5 | ![AWS CDK 2.38.1](https://badgen.net/badge/aws-cdk/2.38.1/yellow) 6 | ![Typescript](https://badgen.net/badge/icon/typescript?icon=typescript&label) 7 | 8 | We are going to run lambda on multiple ram settings to find out which gives you better value for money :moneybag:. 9 | -------------------------------------------------------------------------------- /lambda-cost/Taskfile.yml: -------------------------------------------------------------------------------- 1 | # https://taskfile.dev 2 | 3 | version: '3' 4 | 5 | tasks: 6 | update: 7 | cmds: 8 | - rm -rf node_modules/ 9 | - npm i 10 | fix: 11 | cmds: 12 | - npm audit fix -------------------------------------------------------------------------------- /lambda-cost/bin/lambda-cost.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import 'source-map-support/register'; 3 | import * as cdk from 'aws-cdk-lib'; 4 | import { LambdaCostStack } from '../lib/lambda-cost-stack'; 5 | 6 | const app = new cdk.App(); 7 | new LambdaCostStack(app, 'LambdaCostStack'); 8 | -------------------------------------------------------------------------------- /lambda-cost/cdk.json: -------------------------------------------------------------------------------- 1 | { 2 | "app": "npx ts-node --prefer-ts-exts bin/lambda-cost.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-apigateway:usagePlanKeyOrderInsensitiveId": true, 21 | "@aws-cdk/core:stackRelativeExports": true, 22 | "@aws-cdk/aws-rds:lowercaseDbIdentifier": true, 23 | "@aws-cdk/aws-lambda:recognizeVersionProps": true, 24 | "@aws-cdk/aws-cloudfront:defaultSecurityPolicyTLSv1.2_2021": true 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /lambda-cost/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | roots: ['/test'], 3 | testMatch: ['**/*.test.ts'], 4 | transform: { 5 | '^.+\\.tsx?$': 'ts-jest' 6 | } 7 | }; 8 | -------------------------------------------------------------------------------- /lambda-cost/lambda-fns/create-todo/index.ts: -------------------------------------------------------------------------------- 1 | import { APIGatewayProxyEventV2, APIGatewayProxyResultV2 } from "aws-lambda" 2 | import { v4 as uuid } from "uuid" 3 | import { DynamoDB, PutItemInput } from '@aws-sdk/client-dynamodb' 4 | import { marshall } from '@aws-sdk/util-dynamodb' 5 | 6 | interface TodoInput { 7 | title: string 8 | isComplete: boolean 9 | } 10 | 11 | interface Todo extends TodoInput { 12 | id: string 13 | } 14 | 15 | export async function createTodo(event: APIGatewayProxyEventV2): Promise { 16 | 17 | const body = event.body 18 | 19 | if (!body) { 20 | 21 | return sendFailResponse() 22 | } 23 | 24 | const { title, isComplete } = JSON.parse(body) as TodoInput 25 | 26 | const dynamoClient = new DynamoDB({ region: 'us-east-1' }) 27 | 28 | const newTodo: Todo = { id: uuid(), title, isComplete } 29 | 30 | const newTodoInput: PutItemInput = { 31 | Item: marshall({ 32 | 'id': newTodo.id, 33 | 'title': title, 34 | 'isComplete': isComplete 35 | }), 36 | TableName: process.env.TODO_TABLE_NAME 37 | } 38 | 39 | try { 40 | 41 | await dynamoClient.putItem(newTodoInput) 42 | 43 | return { 44 | statusCode: 200, 45 | body: JSON.stringify({ id: newTodo.id }) 46 | } 47 | 48 | } catch (err) { 49 | 50 | console.log("failed to save new todo", err) 51 | 52 | return sendFailResponse() 53 | } 54 | } 55 | 56 | function sendFailResponse(): APIGatewayProxyResultV2 { 57 | 58 | return { 59 | statusCode: 200, 60 | body: JSON.stringify({ message: 'fail' }) 61 | } 62 | } -------------------------------------------------------------------------------- /lambda-cost/lambda-fns/create-todo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "create-todo", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "@aws-sdk/client-dynamodb": "^3.3.0", 14 | "@aws-sdk/util-dynamodb": "^3.3.0", 15 | "uuid": "^8.3.2" 16 | }, 17 | "devDependencies": { 18 | "@types/aws-lambda": "^8.10.71", 19 | "@types/node": "^14.14.22", 20 | "@types/uuid": "^8.3.0", 21 | "typescript": "^4.1.3" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /lambda-cost/lambda-fns/delete-todo/index.ts: -------------------------------------------------------------------------------- 1 | import { APIGatewayProxyEventV2, APIGatewayProxyResultV2 } from "aws-lambda" 2 | import { DynamoDB, DeleteItemInput } from '@aws-sdk/client-dynamodb' 3 | import { marshall } from '@aws-sdk/util-dynamodb' 4 | 5 | interface Todo { 6 | id: string 7 | } 8 | 9 | export async function deleteTodo(event: APIGatewayProxyEventV2): Promise { 10 | 11 | const body = event.body 12 | 13 | if (!body) { 14 | 15 | return sendFailResponse() 16 | } 17 | 18 | const { id } = JSON.parse(body) as Todo 19 | 20 | const dynamoClient = new DynamoDB({ region: 'us-east-1' }) 21 | 22 | const deleteInput: DeleteItemInput = { 23 | Key: marshall({ id }), 24 | TableName: process.env.TODO_TABLE_NAME 25 | } 26 | 27 | try { 28 | 29 | await dynamoClient.deleteItem(deleteInput) 30 | 31 | return { 32 | statusCode: 200, 33 | body: JSON.stringify({ message: 'success' }) 34 | } 35 | 36 | } catch (err) { 37 | 38 | console.log('failed to delete todo', err) 39 | 40 | return sendFailResponse() 41 | } 42 | } 43 | 44 | function sendFailResponse(): APIGatewayProxyResultV2 { 45 | 46 | return { 47 | statusCode: 200, 48 | body: JSON.stringify({ message: 'fail' }) 49 | } 50 | } -------------------------------------------------------------------------------- /lambda-cost/lambda-fns/delete-todo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "delete-todo", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "@aws-sdk/client-dynamodb": "^3.3.0", 14 | "@aws-sdk/util-dynamodb": "^3.3.0" 15 | }, 16 | "devDependencies": { 17 | "@types/aws-lambda": "^8.10.71", 18 | "@types/node": "^14.14.22", 19 | "typescript": "^4.1.3" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /lambda-cost/lambda-fns/get-todo/index.ts: -------------------------------------------------------------------------------- 1 | import { APIGatewayProxyEventV2, APIGatewayProxyResultV2 } from "aws-lambda" 2 | import { DynamoDB, GetItemInput } from '@aws-sdk/client-dynamodb' 3 | import { marshall, unmarshall } from '@aws-sdk/util-dynamodb' 4 | 5 | interface TodoInput { 6 | id: string 7 | } 8 | 9 | interface Todo extends TodoInput { 10 | title: string 11 | isComplete: boolean 12 | } 13 | 14 | export async function getTodo(event: APIGatewayProxyEventV2): Promise { 15 | 16 | const body = event.body 17 | 18 | if (!body) { 19 | 20 | return sendFailResponse() 21 | } 22 | 23 | const { id } = JSON.parse(body) as TodoInput 24 | 25 | const dynamoClient = new DynamoDB({ region: 'us-east-1' }) 26 | 27 | const getTodoInput: GetItemInput = { 28 | Key: marshall({ id }), 29 | TableName: process.env.TODO_TABLE_NAME 30 | } 31 | 32 | try { 33 | 34 | const { Item } = await dynamoClient.getItem(getTodoInput) 35 | 36 | if (!Item) { 37 | 38 | return sendFailResponse() 39 | } 40 | 41 | const todo = unmarshall(Item) as Todo 42 | 43 | return { 44 | statusCode: 200, 45 | body: JSON.stringify({ todo }) 46 | } 47 | 48 | } catch (err) { 49 | 50 | console.log('get item failed', err) 51 | 52 | return sendFailResponse() 53 | } 54 | } 55 | 56 | function sendFailResponse(): APIGatewayProxyResultV2 { 57 | 58 | return { 59 | statusCode: 400, 60 | body: JSON.stringify({ message: 'fail' }) 61 | } 62 | } -------------------------------------------------------------------------------- /lambda-cost/lambda-fns/get-todo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "get-todo", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "@aws-sdk/client-dynamodb": "^3.3.0", 14 | "@aws-sdk/util-dynamodb": "^3.3.0" 15 | }, 16 | "devDependencies": { 17 | "@types/aws-lambda": "^8.10.71", 18 | "@types/node": "^14.14.22", 19 | "typescript": "^4.1.3" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /lambda-cost/lib/lambda-cost-stack.ts: -------------------------------------------------------------------------------- 1 | import { Stack, StackProps, CfnOutput, RemovalPolicy, Duration } from "aws-cdk-lib" 2 | import { Construct } from "constructs" 3 | import { Architecture, Runtime } from 'aws-cdk-lib/aws-lambda'; 4 | import { NodejsFunction } from 'aws-cdk-lib/aws-lambda-nodejs'; 5 | import { AttributeType, BillingMode, Table } from 'aws-cdk-lib/aws-dynamodb'; 6 | import { CfnApplication } from 'aws-cdk-lib/aws-sam'; 7 | import { todoTableName } from './variables'; 8 | 9 | export class LambdaCostStack extends Stack { 10 | constructor(scope: Construct, id: string, props?: StackProps) { 11 | super(scope, id, props); 12 | 13 | // The code that defines your stack goes here 14 | const powerValues = '128,256,512,1024,2048' 15 | 16 | new CfnApplication(this, 'powerTuner', { 17 | location: { 18 | applicationId: 'arn:aws:serverlessrepo:us-east-1:451282441545:applications/aws-lambda-power-tuning', 19 | semanticVersion: '4.2.1' 20 | }, 21 | parameters: { 22 | "lambdaResource": "*", 23 | "PowerValues": powerValues 24 | }, 25 | timeoutInMinutes: 15 26 | }) 27 | 28 | const todoTable = new Table(this, 'todoTable', { 29 | partitionKey: { 30 | name: 'id', 31 | type: AttributeType.STRING 32 | }, 33 | billingMode: BillingMode.PAY_PER_REQUEST, 34 | removalPolicy: RemovalPolicy.DESTROY 35 | }) 36 | 37 | new CfnOutput(this, 'todoTableName', { 38 | value: todoTable.tableName 39 | }) 40 | 41 | const getTodoFn = new NodejsFunction(this, 'getTodoFn', { 42 | runtime: Runtime.NODEJS_16_X, 43 | entry: `${__dirname}/../lambda-fns/get-todo/index.ts`, 44 | handler: 'getTodo', 45 | architecture: Architecture.ARM_64, 46 | environment: { 47 | TODO_TABLE_NAME: todoTableName 48 | } 49 | }) 50 | 51 | todoTable.grantReadData(getTodoFn) 52 | 53 | const createTodoFn = new NodejsFunction(this, 'createTodoFn', { 54 | runtime: Runtime.NODEJS_16_X, 55 | entry: `${__dirname}/../lambda-fns/create-todo/index.ts`, 56 | handler: 'createTodo', 57 | architecture: Architecture.ARM_64, 58 | environment: { 59 | TODO_TABLE_NAME: todoTableName 60 | } 61 | }) 62 | 63 | todoTable.grantReadWriteData(createTodoFn) 64 | 65 | const deleteTodoFn = new NodejsFunction(this, 'deleteTodoFn', { 66 | runtime: Runtime.NODEJS_16_X, 67 | entry: `${__dirname}/../lambda-fns/delete-todo/index.ts`, 68 | handler: 'deleteTodo', 69 | architecture: Architecture.ARM_64, 70 | environment: { 71 | TODO_TABLE_NAME: todoTableName 72 | } 73 | }) 74 | 75 | todoTable.grantReadWriteData(deleteTodoFn) 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /lambda-cost/lib/variables.ts: -------------------------------------------------------------------------------- 1 | export const todoTableName = 'myTableName' -------------------------------------------------------------------------------- /lambda-cost/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lambda-cost", 3 | "version": "1.1.0", 4 | "bin": { 5 | "lambda-cost": "bin/lambda-cost.js" 6 | }, 7 | "scripts": { 8 | "build": "tsc", 9 | "watch": "tsc -w", 10 | "test": "jest", 11 | "cdk": "cdk" 12 | }, 13 | "devDependencies": { 14 | "@types/jest": "^27.5.0", 15 | "@types/node": "10.17.27", 16 | "jest": "^27.5.1", 17 | "ts-jest": "^27.1.4", 18 | "aws-cdk": "2.38.1", 19 | "ts-node": "^10.7.0", 20 | "typescript": "~3.9.7" 21 | }, 22 | "dependencies": { 23 | "aws-cdk-lib": "2.38.1", 24 | "constructs": "^10.0.0", 25 | "esbuild": "^0.15.5", 26 | "source-map-support": "^0.5.21" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /lambda-cost/test/lambda-cost.test.ts: -------------------------------------------------------------------------------- 1 | import { expect as expectCDK, matchTemplate, MatchStyle } from '@aws-cdk/assert'; 2 | import * as cdk from '@aws-cdk/core'; 3 | import * as LambdaCost from '../lib/lambda-cost-stack'; 4 | 5 | test('Empty Stack', () => { 6 | const app = new cdk.App(); 7 | // WHEN 8 | const stack = new LambdaCost.LambdaCostStack(app, 'MyTestStack'); 9 | // THEN 10 | expectCDK(stack).to(matchTemplate({ 11 | "Resources": {} 12 | }, MatchStyle.EXACT)) 13 | }); 14 | -------------------------------------------------------------------------------- /lambda-cost/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2018", 4 | "module": "commonjs", 5 | "lib": ["es2018"], 6 | "declaration": true, 7 | "strict": true, 8 | "noImplicitAny": true, 9 | "strictNullChecks": true, 10 | "noImplicitThis": true, 11 | "alwaysStrict": true, 12 | "noUnusedLocals": false, 13 | "noUnusedParameters": false, 14 | "noImplicitReturns": true, 15 | "noFallthroughCasesInSwitch": false, 16 | "inlineSourceMap": true, 17 | "inlineSources": true, 18 | "experimentalDecorators": true, 19 | "strictPropertyInitialization": false, 20 | "typeRoots": ["./node_modules/@types"] 21 | }, 22 | "exclude": ["cdk.out"] 23 | } 24 | -------------------------------------------------------------------------------- /lambda-layers/.gitignore: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /lambda-layers/.npmignore: -------------------------------------------------------------------------------- 1 | *.ts 2 | !*.d.ts 3 | 4 | # CDK asset staging directory 5 | .cdk.staging 6 | cdk.out 7 | -------------------------------------------------------------------------------- /lambda-layers/README.md: -------------------------------------------------------------------------------- 1 | # [Lambda Layers](https://apoorv.blog/lambda-layers-cdk/) 2 | 3 | [![Read Tutorial](https://badgen.now.sh/badge/Read/Tutorial/purple)](https://apoorv.blog/lambda-layers-cdk/) 4 | [![MIT License](https://badgen.now.sh/badge/License/MIT/blue)](https://github.com/apoorvmote/cdk-examples/blob/master/License.md) 5 | ![AWS CDK 2.38.1](https://badgen.net/badge/aws-cdk/2.38.1/yellow) 6 | ![Typescript](https://badgen.net/badge/icon/typescript?icon=typescript&label) 7 | 8 | Create lambda layers to update multiple lambdas in one go. Only create layers who's code or dependency updates frequently. -------------------------------------------------------------------------------- /lambda-layers/Taskfile.yml: -------------------------------------------------------------------------------- 1 | # https://taskfile.dev 2 | 3 | version: '3' 4 | 5 | tasks: 6 | update: 7 | cmds: 8 | - rm -rf node_modules/ 9 | - npm i 10 | fix: 11 | cmds: 12 | - npm audit fix -------------------------------------------------------------------------------- /lambda-layers/bin/lambda-layers.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import 'source-map-support/register'; 3 | import * as cdk from 'aws-cdk-lib'; 4 | import { LambdaLayersStack } from '../lib/lambda-layers-stack'; 5 | import { virginia } from '../lib/variables'; 6 | 7 | const app = new cdk.App(); 8 | new LambdaLayersStack(app, 'LambdaLayersStack', { env: virginia }); 9 | -------------------------------------------------------------------------------- /lambda-layers/cdk.json: -------------------------------------------------------------------------------- 1 | { 2 | "app": "npx ts-node --prefer-ts-exts bin/lambda-layers.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-apigateway:usagePlanKeyOrderInsensitiveId": true, 21 | "@aws-cdk/core:stackRelativeExports": true, 22 | "@aws-cdk/aws-rds:lowercaseDbIdentifier": true, 23 | "@aws-cdk/aws-lambda:recognizeVersionProps": true, 24 | "@aws-cdk/aws-cloudfront:defaultSecurityPolicyTLSv1.2_2021": true 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /lambda-layers/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | roots: ['/test'], 3 | testMatch: ['**/*.test.ts'], 4 | transform: { 5 | '^.+\\.tsx?$': 'ts-jest' 6 | } 7 | }; 8 | -------------------------------------------------------------------------------- /lambda-layers/lambda-fns/layer/Taskfile.yml: -------------------------------------------------------------------------------- 1 | # https://taskfile.dev 2 | 3 | version: '3' 4 | 5 | tasks: 6 | default: 7 | cmds: 8 | - zip -r deployment.zip nodejs -------------------------------------------------------------------------------- /lambda-layers/lambda-fns/layer/deployment.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apoorvmote/cdk-examples/4fb551420a2da0ed2e186a133ebf2bb2fb54c38d/lambda-layers/lambda-fns/layer/deployment.zip -------------------------------------------------------------------------------- /lambda-layers/lambda-fns/layer/nodejs/discount.js: -------------------------------------------------------------------------------- 1 | exports.discountForProduct = (productId) => { 2 | 3 | const products = [ 4 | { 5 | productId: '1', 6 | discount: '10%' 7 | }, 8 | { 9 | productId: '2', 10 | discount: '20%' 11 | }, 12 | { 13 | productId: '3', 14 | discount: '30%' 15 | } 16 | ] 17 | 18 | const item = products.find(element => element.productId === productId) 19 | 20 | return item.discount 21 | } -------------------------------------------------------------------------------- /lambda-layers/lambda-fns/one/Taskfile.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | tasks: 4 | default: 5 | cmds: 6 | - rm deployment.zip -f 7 | - zip deployment.zip index.js -------------------------------------------------------------------------------- /lambda-layers/lambda-fns/one/deployment.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apoorvmote/cdk-examples/4fb551420a2da0ed2e186a133ebf2bb2fb54c38d/lambda-layers/lambda-fns/one/deployment.zip -------------------------------------------------------------------------------- /lambda-layers/lambda-fns/one/index.js: -------------------------------------------------------------------------------- 1 | const uuid = require('uuid/v4') 2 | const { discountForProduct } = require('./discount') 3 | 4 | exports.handler = async(event) => { 5 | 6 | const { productId } = event.queryStringParameters 7 | 8 | return { 9 | statusCode: 200, 10 | body: JSON.stringify({ 11 | transactionId: uuid(), 12 | discount: discountForProduct(productId), 13 | message: 'from lambda one' 14 | }) 15 | } 16 | } -------------------------------------------------------------------------------- /lambda-layers/lambda-fns/two/Taskfile.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | tasks: 4 | default: 5 | cmds: 6 | - rm deployment.zip -f 7 | - zip deployment.zip index.js -------------------------------------------------------------------------------- /lambda-layers/lambda-fns/two/deployment.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apoorvmote/cdk-examples/4fb551420a2da0ed2e186a133ebf2bb2fb54c38d/lambda-layers/lambda-fns/two/deployment.zip -------------------------------------------------------------------------------- /lambda-layers/lambda-fns/two/index.js: -------------------------------------------------------------------------------- 1 | const uuid = require('uuid/v4') 2 | const { discountForProduct } = require('./discount') 3 | 4 | exports.handler = async(event) => { 5 | 6 | const { productId } = event.queryStringParameters 7 | 8 | return { 9 | statusCode: 200, 10 | body: JSON.stringify({ 11 | transactionId: uuid(), 12 | discount: discountForProduct(productId), 13 | message: 'from lambda two' 14 | }) 15 | } 16 | } -------------------------------------------------------------------------------- /lambda-layers/lib/lambda-layers-stack.ts: -------------------------------------------------------------------------------- 1 | import { Stack, StackProps, CfnOutput } from "aws-cdk-lib" 2 | import { Construct } from "constructs" 3 | import { Architecture, Code, Function, LayerVersion, Runtime } from 'aws-cdk-lib/aws-lambda'; 4 | import { account, layerVersionArn } from './variables'; 5 | 6 | export class LambdaLayersStack extends Stack { 7 | constructor(scope: Construct, id: string, props?: StackProps) { 8 | super(scope, id, props); 9 | 10 | // The code that defines your stack goes here 11 | 12 | const layer = new LayerVersion(this, 'uuidLayer', { 13 | code: Code.fromAsset(`${__dirname}/../lambda-fns/layer/deployment.zip`), 14 | compatibleRuntimes: [ Runtime.NODEJS_16_X], 15 | compatibleArchitectures: [Architecture.ARM_64], 16 | description: 'uuid package and discount for product' 17 | }) 18 | 19 | layer.addPermission('layerPermission', { 20 | accountId: account 21 | }) 22 | 23 | new CfnOutput(this, 'layerVersionArn', { 24 | value: layer.layerVersionArn 25 | }) 26 | 27 | const latestLayer = LayerVersion.fromLayerVersionAttributes(this, 'latestLayer', { 28 | compatibleRuntimes: [Runtime.NODEJS_16_X], 29 | layerVersionArn 30 | }) 31 | 32 | new Function(this, 'oneFn', { 33 | runtime: Runtime.NODEJS_16_X, 34 | code: Code.fromAsset(`${__dirname}/../lambda-fns/one/deployment.zip`), 35 | handler: 'index.handler', 36 | architecture: Architecture.ARM_64, 37 | layers: [latestLayer] 38 | }) 39 | 40 | new Function(this, 'twoFn', { 41 | runtime: Runtime.NODEJS_16_X, 42 | code: Code.fromAsset(`${__dirname}/../lambda-fns/two/deployment.zip`), 43 | handler: 'index.handler', 44 | architecture: Architecture.ARM_64, 45 | layers: [latestLayer] 46 | }) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /lambda-layers/lib/variables.ts: -------------------------------------------------------------------------------- 1 | export const account = '1234567890' 2 | 3 | export const virginia = { account, region: 'us-east-1' } 4 | 5 | export const layerVersionArn = 'arn:aws:lambda:us-east-1:1234567890:layer:uuidLayer222C238D:1' -------------------------------------------------------------------------------- /lambda-layers/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lambda-layers", 3 | "version": "1.1.0", 4 | "bin": { 5 | "lambda-layers": "bin/lambda-layers.js" 6 | }, 7 | "scripts": { 8 | "build": "tsc", 9 | "watch": "tsc -w", 10 | "test": "jest", 11 | "cdk": "cdk" 12 | }, 13 | "devDependencies": { 14 | "@types/jest": "^27.5.0", 15 | "@types/node": "10.17.27", 16 | "jest": "^27.5.1", 17 | "ts-jest": "^27.1.4", 18 | "aws-cdk": "2.38.1", 19 | "ts-node": "^10.7.0", 20 | "typescript": "~3.9.7" 21 | }, 22 | "dependencies": { 23 | "aws-cdk-lib": "2.38.1", 24 | "constructs": "^10.0.0", 25 | "source-map-support": "^0.5.21" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /lambda-layers/test/lambda-layers.test.ts: -------------------------------------------------------------------------------- 1 | import { expect as expectCDK, matchTemplate, MatchStyle } from '@aws-cdk/assert'; 2 | import * as cdk from '@aws-cdk/core'; 3 | import * as LambdaLayers from '../lib/lambda-layers-stack'; 4 | 5 | test('Empty Stack', () => { 6 | const app = new cdk.App(); 7 | // WHEN 8 | const stack = new LambdaLayers.LambdaLayersStack(app, 'MyTestStack'); 9 | // THEN 10 | expectCDK(stack).to(matchTemplate({ 11 | "Resources": {} 12 | }, MatchStyle.EXACT)) 13 | }); 14 | -------------------------------------------------------------------------------- /lambda-layers/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2018", 4 | "module": "commonjs", 5 | "lib": ["es2018"], 6 | "declaration": true, 7 | "strict": true, 8 | "noImplicitAny": true, 9 | "strictNullChecks": true, 10 | "noImplicitThis": true, 11 | "alwaysStrict": true, 12 | "noUnusedLocals": false, 13 | "noUnusedParameters": false, 14 | "noImplicitReturns": true, 15 | "noFallthroughCasesInSwitch": false, 16 | "inlineSourceMap": true, 17 | "inlineSources": true, 18 | "experimentalDecorators": true, 19 | "strictPropertyInitialization": false, 20 | "typeRoots": ["./node_modules/@types"] 21 | }, 22 | "exclude": ["cdk.out"] 23 | } 24 | -------------------------------------------------------------------------------- /lambda-local/.gitignore: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /lambda-local/.npmignore: -------------------------------------------------------------------------------- 1 | *.ts 2 | !*.d.ts 3 | 4 | # CDK asset staging directory 5 | .cdk.staging 6 | cdk.out 7 | -------------------------------------------------------------------------------- /lambda-local/README.md: -------------------------------------------------------------------------------- 1 | # [Lambda Local](https://apoorv.blog/run-lambda-locally-cdk-sam/) 2 | 3 | [![Read Tutorial](https://badgen.now.sh/badge/Read/Tutorial/purple)](https://apoorv.blog/run-lambda-locally-cdk-sam/) 4 | [![MIT License](https://badgen.now.sh/badge/License/MIT/blue)](https://github.com/apoorvmote/cdk-examples/blob/master/License.md) 5 | ![AWS CDK 2.38.1](https://badgen.net/badge/aws-cdk/2.38.1/yellow) 6 | ![Typescript](https://badgen.net/badge/icon/typescript?icon=typescript&label) 7 | 8 | Lambda that is deployed with CDK can be invoked locally with SAM for faster iteration. -------------------------------------------------------------------------------- /lambda-local/Taskfile.yml: -------------------------------------------------------------------------------- 1 | # https://taskfile.dev 2 | 3 | version: '3' 4 | 5 | tasks: 6 | update: 7 | cmds: 8 | - rm -rf node_modules/ 9 | - npm i 10 | fix: 11 | cmds: 12 | - npm audit fix -------------------------------------------------------------------------------- /lambda-local/bin/lambda-local.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import 'source-map-support/register'; 3 | import * as cdk from 'aws-cdk-lib'; 4 | import { LambdaLocalStack } from '../lib/lambda-local-stack'; 5 | import { virginia } from '../lib/variables'; 6 | 7 | const app = new cdk.App(); 8 | new LambdaLocalStack(app, 'LambdaLocalStack-WDzD8ERde7sB6sfJ', { env: virginia }); 9 | -------------------------------------------------------------------------------- /lambda-local/cdk.json: -------------------------------------------------------------------------------- 1 | { 2 | "app": "npx ts-node --prefer-ts-exts bin/lambda-local.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-apigateway:usagePlanKeyOrderInsensitiveId": true, 21 | "@aws-cdk/core:stackRelativeExports": true, 22 | "@aws-cdk/aws-rds:lowercaseDbIdentifier": true, 23 | "@aws-cdk/aws-lambda:recognizeVersionProps": true, 24 | "@aws-cdk/aws-cloudfront:defaultSecurityPolicyTLSv1.2_2021": true 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /lambda-local/events/hello.json: -------------------------------------------------------------------------------- 1 | { 2 | "givenName": "Apoorv", 3 | "familyName": "Mote" 4 | } -------------------------------------------------------------------------------- /lambda-local/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | roots: ['/test'], 3 | testMatch: ['**/*.test.ts'], 4 | transform: { 5 | '^.+\\.tsx?$': 'ts-jest' 6 | } 7 | }; 8 | -------------------------------------------------------------------------------- /lambda-local/lambda-fns/one/Taskfile.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | tasks: 4 | default: 5 | cmds: 6 | - rm deployment.zip -f 7 | - zip deployment.zip index.js -------------------------------------------------------------------------------- /lambda-local/lambda-fns/one/deployment.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apoorvmote/cdk-examples/4fb551420a2da0ed2e186a133ebf2bb2fb54c38d/lambda-local/lambda-fns/one/deployment.zip -------------------------------------------------------------------------------- /lambda-local/lambda-fns/one/index.js: -------------------------------------------------------------------------------- 1 | exports.handler = async (event) => { 2 | 3 | console.log(event) 4 | 5 | return { 6 | statusCode: 200, 7 | body: JSON.stringify({ message: 'success' }) 8 | } 9 | } -------------------------------------------------------------------------------- /lambda-local/lib/lambda-local-stack.ts: -------------------------------------------------------------------------------- 1 | import { Stack, StackProps, CfnOutput, RemovalPolicy, Duration } from "aws-cdk-lib" 2 | import { Construct } from "constructs" 3 | import { Code, Function, Runtime } from 'aws-cdk-lib/aws-lambda'; 4 | 5 | export class LambdaLocalStack extends Stack { 6 | constructor(scope: Construct, id: string, props?: StackProps) { 7 | super(scope, id, props); 8 | 9 | // The code that defines your stack goes here 10 | 11 | const oneFn = new Function(this, 'oneFn', { 12 | runtime: Runtime.NODEJS_16_X, 13 | code: Code.fromAsset(`${__dirname}/../lambda-fns/one/deployment.zip`), 14 | handler: 'index.handler' 15 | }) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /lambda-local/lib/variables.ts: -------------------------------------------------------------------------------- 1 | export const virginia = { account: '1234567890', region: 'us-east-1' } -------------------------------------------------------------------------------- /lambda-local/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lambda-local", 3 | "version": "1.1.0", 4 | "bin": { 5 | "lambda-local": "bin/lambda-local.js" 6 | }, 7 | "scripts": { 8 | "build": "tsc", 9 | "watch": "tsc -w", 10 | "test": "jest", 11 | "cdk": "cdk" 12 | }, 13 | "devDependencies": { 14 | "@types/jest": "^27.5.0", 15 | "@types/node": "10.17.27", 16 | "jest": "^27.5.1", 17 | "ts-jest": "^27.1.4", 18 | "aws-cdk": "2.38.1", 19 | "ts-node": "^10.7.0", 20 | "typescript": "~3.9.7" 21 | }, 22 | "dependencies": { 23 | "aws-cdk-lib": "2.38.1", 24 | "constructs": "^10.0.0", 25 | "source-map-support": "^0.5.21" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /lambda-local/test/lambda-local.test.ts: -------------------------------------------------------------------------------- 1 | import { expect as expectCDK, matchTemplate, MatchStyle } from '@aws-cdk/assert'; 2 | import * as cdk from '@aws-cdk/core'; 3 | import * as LambdaLocal from '../lib/lambda-local-stack'; 4 | 5 | test('Empty Stack', () => { 6 | const app = new cdk.App(); 7 | // WHEN 8 | const stack = new LambdaLocal.LambdaLocalStack(app, 'MyTestStack'); 9 | // THEN 10 | expectCDK(stack).to(matchTemplate({ 11 | "Resources": {} 12 | }, MatchStyle.EXACT)) 13 | }); 14 | -------------------------------------------------------------------------------- /lambda-local/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2018", 4 | "module": "commonjs", 5 | "lib": ["es2018"], 6 | "declaration": true, 7 | "strict": true, 8 | "noImplicitAny": true, 9 | "strictNullChecks": true, 10 | "noImplicitThis": true, 11 | "alwaysStrict": true, 12 | "noUnusedLocals": false, 13 | "noUnusedParameters": false, 14 | "noImplicitReturns": true, 15 | "noFallthroughCasesInSwitch": false, 16 | "inlineSourceMap": true, 17 | "inlineSources": true, 18 | "experimentalDecorators": true, 19 | "strictPropertyInitialization": false, 20 | "typeRoots": ["./node_modules/@types"] 21 | }, 22 | "exclude": ["cdk.out"] 23 | } 24 | -------------------------------------------------------------------------------- /nextjs-pipeline/Readme.md: -------------------------------------------------------------------------------- 1 | # [deploy NextJS with Fargate ECS with Code Pipeline](https://apoorv.blog/nextjs-fargate-codepipline-cdk/) 2 | 3 | [![Read Tutorial](https://badgen.now.sh/badge/Read/Tutorial/purple)](https://apoorv.blog/nextjs-fargate-codepipline-cdk/) 4 | [![MIT License](https://badgen.now.sh/badge/License/MIT/blue)](https://github.com/apoorvmote/cdk-examples/blob/master/License.md) 5 | ![AWS CDK 2.38.1](https://badgen.net/badge/aws-cdk/2.38.1/yellow) 6 | ![Typescript](https://badgen.net/badge/icon/typescript?icon=typescript&label) 7 | 8 | **Important** This is only applicable for Server side rendered NextJS app. This is not applicable for Statically generated NextJS app. For that simply deploy to any CDN and be done with it. -------------------------------------------------------------------------------- /nextjs-pipeline/backend-infra/.gitignore: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /nextjs-pipeline/backend-infra/.npmignore: -------------------------------------------------------------------------------- 1 | *.ts 2 | !*.d.ts 3 | 4 | # CDK asset staging directory 5 | .cdk.staging 6 | cdk.out 7 | -------------------------------------------------------------------------------- /nextjs-pipeline/backend-infra/README.md: -------------------------------------------------------------------------------- 1 | # Welcome to your CDK TypeScript project! 2 | 3 | This is a blank project for TypeScript development with CDK. 4 | 5 | The `cdk.json` file tells the CDK Toolkit how to execute your app. 6 | 7 | ## Useful commands 8 | 9 | * `npm run build` compile typescript to js 10 | * `npm run watch` watch for changes and compile 11 | * `npm run test` perform the jest unit tests 12 | * `cdk deploy` deploy this stack to your default AWS account/region 13 | * `cdk diff` compare deployed stack with current state 14 | * `cdk synth` emits the synthesized CloudFormation template 15 | -------------------------------------------------------------------------------- /nextjs-pipeline/backend-infra/Taskfile.yml: -------------------------------------------------------------------------------- 1 | # https://taskfile.dev 2 | 3 | version: '3' 4 | 5 | tasks: 6 | update: 7 | cmds: 8 | - rm -rf node_modules/ 9 | - npm i 10 | fix: 11 | cmds: 12 | - npm audit fix -------------------------------------------------------------------------------- /nextjs-pipeline/backend-infra/bin/backend-infra.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import 'source-map-support/register'; 3 | import * as cdk from 'aws-cdk-lib'; 4 | import { BackendInfraStack } from '../lib/backend-infra-stack'; 5 | import { virginia } from '../lib/variables'; 6 | import { WOLoadBalancerStack } from '../lib/witout-load-balancer'; 7 | 8 | const app = new cdk.App(); 9 | 10 | new BackendInfraStack(app, 'BackendInfraStack', { env: virginia }); 11 | 12 | new WOLoadBalancerStack(app, 'withoutLoadBalancer', { env: virginia }) 13 | -------------------------------------------------------------------------------- /nextjs-pipeline/backend-infra/cdk.json: -------------------------------------------------------------------------------- 1 | { 2 | "app": "npx ts-node --prefer-ts-exts bin/backend-infra.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-apigateway:usagePlanKeyOrderInsensitiveId": true, 21 | "@aws-cdk/core:stackRelativeExports": true, 22 | "@aws-cdk/aws-rds:lowercaseDbIdentifier": true, 23 | "@aws-cdk/aws-lambda:recognizeVersionProps": true, 24 | "@aws-cdk/aws-cloudfront:defaultSecurityPolicyTLSv1.2_2021": true 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /nextjs-pipeline/backend-infra/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | roots: ['/test'], 3 | testMatch: ['**/*.test.ts'], 4 | transform: { 5 | '^.+\\.tsx?$': 'ts-jest' 6 | } 7 | }; 8 | -------------------------------------------------------------------------------- /nextjs-pipeline/backend-infra/lib/backend-infra-stack.ts: -------------------------------------------------------------------------------- 1 | import { Stack, StackProps, CfnOutput, RemovalPolicy, Duration } from "aws-cdk-lib" 2 | import { Construct } from "constructs" 3 | import { Repository } from 'aws-cdk-lib/aws-ecr'; 4 | import { Vpc } from 'aws-cdk-lib/aws-ec2' 5 | import { Cluster, ContainerImage, DeploymentControllerType } from 'aws-cdk-lib/aws-ecs'; 6 | import { HostedZone } from 'aws-cdk-lib/aws-route53'; 7 | import { website_domain, hostedZoneId, dockerUsername, dockerPassword } from './variables'; 8 | import { ApplicationLoadBalancedFargateService } from 'aws-cdk-lib/aws-ecs-patterns'; 9 | import { ApplicationProtocol } from 'aws-cdk-lib/aws-elasticloadbalancingv2'; 10 | import { HttpsRedirect } from 'aws-cdk-lib/aws-route53-patterns'; 11 | import * as codecommit from 'aws-cdk-lib/aws-codecommit' 12 | import { BuildSpec, ComputeType, LinuxBuildImage, PipelineProject } from 'aws-cdk-lib/aws-codebuild'; 13 | import { Bucket } from 'aws-cdk-lib/aws-s3'; 14 | import { Artifact, Pipeline } from 'aws-cdk-lib/aws-codepipeline'; 15 | import { CodeBuildAction, CodeCommitSourceAction, EcsDeployAction } from 'aws-cdk-lib/aws-codepipeline-actions'; 16 | 17 | 18 | export class BackendInfraStack extends Stack { 19 | constructor(scope: Construct, id: string, props?: StackProps) { 20 | super(scope, id, props); 21 | 22 | // The code that defines your stack goes here 23 | 24 | const ecrRepo = new Repository(this, 'nextJSRepo', { 25 | repositoryName: 'next-starter', 26 | removalPolicy: RemovalPolicy.DESTROY, 27 | imageScanOnPush: true, 28 | lifecycleRules: [ 29 | { 30 | tagPrefixList: ['prod'], 31 | maxImageCount: 5 32 | }, 33 | { 34 | maxImageAge: Duration.days(30) 35 | } 36 | ] 37 | }) 38 | 39 | new CfnOutput(this, 'repoUrl', { 40 | value: ecrRepo.repositoryUri 41 | }) 42 | 43 | const vpc = new Vpc(this, 'fargateVpc', { 44 | maxAzs: 2 45 | }) 46 | 47 | const cluster = new Cluster(this, 'fargateCluster', { 48 | clusterName: 'fargateCluster', 49 | containerInsights: true, 50 | vpc 51 | }) 52 | 53 | const domainZone = HostedZone.fromHostedZoneAttributes(this, 'hostedZoneWithAttributes', { 54 | zoneName: website_domain, 55 | hostedZoneId 56 | }) 57 | 58 | const fargateService = new ApplicationLoadBalancedFargateService(this, 'fargateService', { 59 | cluster, 60 | assignPublicIp: true, 61 | cpu: 256, 62 | desiredCount: 1, 63 | memoryLimitMiB: 512, 64 | redirectHTTP: true, 65 | protocol: ApplicationProtocol.HTTPS, 66 | deploymentController: { 67 | type: DeploymentControllerType.ECS 68 | }, 69 | domainName: website_domain, 70 | domainZone, 71 | taskImageOptions: { 72 | // image from docker hub 73 | // image: ContainerImage.fromRegistry('apoorvmote/next:prod-v1'), 74 | // image from public ecr registry 75 | // image: ContainerImage.fromRegistry('public.ecr.aws/abc123xyz/next:prod-v1'), 76 | // image from private ecr registry 77 | image: ContainerImage.fromEcrRepository(ecrRepo, 'prod-v1'), 78 | } 79 | }) 80 | 81 | const scalableTarget = fargateService.service.autoScaleTaskCount({ 82 | minCapacity: 1, 83 | maxCapacity: 20 84 | }) 85 | 86 | scalableTarget.scaleOnCpuUtilization('cpuScaling', { 87 | targetUtilizationPercent: 70 88 | }) 89 | 90 | new HttpsRedirect(this, 'wwwToNonWww', { 91 | recordNames: ['www.example.com'], 92 | targetDomain: website_domain, 93 | zone: domainZone 94 | }) 95 | 96 | const nextRepo = new codecommit.Repository(this, 'nextJSSourceCode', { 97 | repositoryName: 'next-blog', 98 | description: 'Pipeline source code' 99 | }) 100 | 101 | new CfnOutput(this, 'sourceCodeUrl', { 102 | value: nextRepo.repositoryCloneUrlSsh 103 | }) 104 | 105 | const containerBuildProject = new PipelineProject(this, 'containerBuild', { 106 | buildSpec: BuildSpec.fromSourceFilename('buildspec.yml'), 107 | environment: { 108 | buildImage: LinuxBuildImage.STANDARD_5_0, 109 | computeType: ComputeType.SMALL, 110 | privileged: true 111 | }, 112 | environmentVariables: { 113 | 'docker_username': { 114 | value: dockerUsername 115 | }, 116 | 'docker_password': { 117 | value: dockerPassword 118 | } 119 | } 120 | }) 121 | 122 | ecrRepo.grantPullPush(containerBuildProject.grantPrincipal) 123 | 124 | const artifactBucket = new Bucket(this, 'containerBuildArtifactBucket', { 125 | bucketName: 'example-pipeline-artifact', 126 | removalPolicy: RemovalPolicy.DESTROY 127 | }) 128 | 129 | const gitOutput = new Artifact('nextJSLatestMaster') 130 | 131 | const buildOutput = new Artifact('ContainerBuildOutput') 132 | 133 | new Pipeline(this, 'containerPipeline', { 134 | artifactBucket, 135 | pipelineName: 'examplePipeline', 136 | stages: [ 137 | { 138 | stageName: 'SourceCode', 139 | actions: [ 140 | new CodeCommitSourceAction({ 141 | actionName: 'readCode', 142 | output: gitOutput, 143 | repository: nextRepo, 144 | branch: 'main' 145 | }) 146 | ] 147 | }, 148 | { 149 | stageName: 'build', 150 | actions: [ 151 | new CodeBuildAction({ 152 | actionName: 'buildContainer', 153 | input: gitOutput, 154 | outputs: [buildOutput], 155 | project: containerBuildProject 156 | }) 157 | ] 158 | }, 159 | { 160 | stageName: 'deploy', 161 | actions: [ 162 | new EcsDeployAction({ 163 | actionName: 'deployContainer', 164 | service: fargateService.service, 165 | input: buildOutput, 166 | deploymentTimeout: Duration.minutes(30) 167 | }) 168 | ] 169 | } 170 | ] 171 | }) 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /nextjs-pipeline/backend-infra/lib/variables.ts: -------------------------------------------------------------------------------- 1 | export const website_domain = 'example.com' 2 | 3 | export const hostedZoneId = 'ABCDEFIGH' 4 | 5 | export const dockerUsername = 'username' 6 | 7 | export const dockerPassword = 'password' 8 | 9 | export const virginia = { account: '012345678901', region: 'us-east-1' } -------------------------------------------------------------------------------- /nextjs-pipeline/backend-infra/lib/witout-load-balancer.ts: -------------------------------------------------------------------------------- 1 | import { Stack, StackProps } from "aws-cdk-lib" 2 | import { Construct } from "constructs" 3 | import { Peer, Port, SecurityGroup, Vpc } from 'aws-cdk-lib/aws-ec2'; 4 | import { Cluster, ContainerImage, FargateService, FargateTaskDefinition } from 'aws-cdk-lib/aws-ecs'; 5 | 6 | export class WOLoadBalancerStack extends Stack { 7 | constructor(scope: Construct, id: string, props?: StackProps) { 8 | super(scope, id, props); 9 | 10 | // The code that defines your stack goes here 11 | 12 | const vpc = new Vpc(this, 'fargateVpc', { 13 | maxAzs: 2 14 | }) 15 | 16 | const securityGroup = new SecurityGroup(this, 'fargateSecurity', { 17 | vpc, 18 | securityGroupName: 'fargateSecurity' 19 | }) 20 | 21 | securityGroup.addIngressRule(Peer.anyIpv4(), Port.tcp(80)) 22 | 23 | const cluster = new Cluster(this, 'nextJSCluster', { 24 | clusterName: 'exampleCluster', 25 | containerInsights: true, 26 | vpc 27 | }) 28 | 29 | const taskDefinition = new FargateTaskDefinition(this, 'nextJSTaskDefinition', { 30 | memoryLimitMiB: 512, 31 | cpu: 256 32 | }) 33 | 34 | const container = taskDefinition.addContainer('nextJSContainer', { 35 | image: ContainerImage.fromRegistry('public.ecr.aws/abc123xyz/next:prod-v1'), 36 | }) 37 | 38 | container.addPortMappings({ 39 | containerPort: 80, 40 | hostPort: 80 41 | }) 42 | 43 | new FargateService(this, 'NextJSService', { 44 | taskDefinition, 45 | cluster, 46 | desiredCount: 1, 47 | serviceName: 'exampleService', 48 | assignPublicIp: true, 49 | securityGroups:[securityGroup] 50 | }) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /nextjs-pipeline/backend-infra/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "backend-infra", 3 | "version": "1.1.0", 4 | "bin": { 5 | "backend-infra": "bin/backend-infra.js" 6 | }, 7 | "scripts": { 8 | "build": "tsc", 9 | "watch": "tsc -w", 10 | "test": "jest", 11 | "cdk": "cdk" 12 | }, 13 | "devDependencies": { 14 | "@types/jest": "^27.5.0", 15 | "@types/node": "10.17.27", 16 | "jest": "^27.5.1", 17 | "ts-jest": "^27.1.4", 18 | "aws-cdk": "2.38.1", 19 | "ts-node": "^10.7.0", 20 | "typescript": "~3.9.7" 21 | }, 22 | "dependencies": { 23 | "aws-cdk-lib": "2.38.1", 24 | "constructs": "^10.0.0", 25 | "source-map-support": "^0.5.21" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /nextjs-pipeline/backend-infra/test/backend-infra.test.ts: -------------------------------------------------------------------------------- 1 | import { expect as expectCDK, matchTemplate, MatchStyle } from '@aws-cdk/assert'; 2 | import * as cdk from '@aws-cdk/core'; 3 | import * as BackendInfra from '../lib/backend-infra-stack'; 4 | 5 | test('Empty Stack', () => { 6 | const app = new cdk.App(); 7 | // WHEN 8 | const stack = new BackendInfra.BackendInfraStack(app, 'MyTestStack'); 9 | // THEN 10 | expectCDK(stack).to(matchTemplate({ 11 | "Resources": {} 12 | }, MatchStyle.EXACT)) 13 | }); 14 | -------------------------------------------------------------------------------- /nextjs-pipeline/backend-infra/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2018", 4 | "module": "commonjs", 5 | "lib": ["es2018"], 6 | "declaration": true, 7 | "strict": true, 8 | "noImplicitAny": true, 9 | "strictNullChecks": true, 10 | "noImplicitThis": true, 11 | "alwaysStrict": true, 12 | "noUnusedLocals": false, 13 | "noUnusedParameters": false, 14 | "noImplicitReturns": true, 15 | "noFallthroughCasesInSwitch": false, 16 | "inlineSourceMap": true, 17 | "inlineSources": true, 18 | "experimentalDecorators": true, 19 | "strictPropertyInitialization": false, 20 | "typeRoots": ["./node_modules/@types"] 21 | }, 22 | "exclude": ["cdk.out"] 23 | } 24 | -------------------------------------------------------------------------------- /nextjs-pipeline/next-client/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | 27 | # local env files 28 | .env.local 29 | .env.development.local 30 | .env.test.local 31 | .env.production.local 32 | 33 | # vercel 34 | .vercel 35 | -------------------------------------------------------------------------------- /nextjs-pipeline/next-client/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:14.15.1-alpine3.12 2 | 3 | RUN mkdir -p /usr/src/app 4 | 5 | WORKDIR /usr/src/app 6 | 7 | COPY package.json /usr/src/app 8 | 9 | COPY package-lock.json /usr/src/app 10 | 11 | RUN npm install --production 12 | 13 | COPY . /usr/src/app 14 | 15 | RUN npm run build 16 | 17 | EXPOSE 80 18 | 19 | CMD [ "npm", "start" ] -------------------------------------------------------------------------------- /nextjs-pipeline/next-client/README.md: -------------------------------------------------------------------------------- 1 | This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). 2 | 3 | ## Getting Started 4 | 5 | First, run the development server: 6 | 7 | ```bash 8 | npm run dev 9 | # or 10 | yarn dev 11 | ``` 12 | 13 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. 14 | 15 | You can start editing the page by modifying `pages/index.js`. The page auto-updates as you edit the file. 16 | 17 | ## Learn More 18 | 19 | To learn more about Next.js, take a look at the following resources: 20 | 21 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. 22 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. 23 | 24 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! 25 | 26 | ## Deploy on Vercel 27 | 28 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/import?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. 29 | 30 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. 31 | -------------------------------------------------------------------------------- /nextjs-pipeline/next-client/buildspec.yml: -------------------------------------------------------------------------------- 1 | version: 0.2 2 | 3 | phases: 4 | pre_build: 5 | commands: 6 | - echo Logging in to Amazon ECR... 7 | - aws --version 8 | - $(aws ecr get-login --region $AWS_DEFAULT_REGION --no-include-email) 9 | - docker login -u $docker_username -p $docker_password 10 | - REPOSITORY_URI=1234567890.dkr.ecr.us-east-1.amazonaws.com/next-starter 11 | - COMMIT_HASH=prod-$(echo $CODEBUILD_RESOLVED_SOURCE_VERSION | cut -c 1-7) 12 | - IMAGE_TAG=${COMMIT_HASH:=latest} 13 | build: 14 | commands: 15 | - echo Build started on `date` 16 | - echo Building the Docker image... 17 | - docker build -t $REPOSITORY_URI:latest . 18 | - docker tag $REPOSITORY_URI:latest $REPOSITORY_URI:$IMAGE_TAG 19 | post_build: 20 | commands: 21 | - echo Build completed on `date` 22 | - echo Pushing the Docker images... 23 | - docker push $REPOSITORY_URI:$IMAGE_TAG 24 | - echo Writing image definitions file... 25 | - printf '[{"name":"web","imageUri":"%s"}]' $REPOSITORY_URI:$IMAGE_TAG > imagedefinitions.json 26 | artifacts: 27 | files: imagedefinitions.json -------------------------------------------------------------------------------- /nextjs-pipeline/next-client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "next-client", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start -p 80" 9 | }, 10 | "dependencies": { 11 | "next": "10.0.3", 12 | "react": "17.0.1", 13 | "react-dom": "17.0.1" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /nextjs-pipeline/next-client/pages/_app.js: -------------------------------------------------------------------------------- 1 | import '../styles/globals.css' 2 | 3 | function MyApp({ Component, pageProps }) { 4 | return 5 | } 6 | 7 | export default MyApp 8 | -------------------------------------------------------------------------------- /nextjs-pipeline/next-client/pages/api/hello.js: -------------------------------------------------------------------------------- 1 | // Next.js API route support: https://nextjs.org/docs/api-routes/introduction 2 | 3 | export default (req, res) => { 4 | res.statusCode = 200 5 | res.json({ name: 'John Doe' }) 6 | } 7 | -------------------------------------------------------------------------------- /nextjs-pipeline/next-client/pages/index.js: -------------------------------------------------------------------------------- 1 | import Head from 'next/head' 2 | import styles from '../styles/Home.module.css' 3 | 4 | export default function Home() { 5 | return ( 6 | 64 | ) 65 | } 66 | -------------------------------------------------------------------------------- /nextjs-pipeline/next-client/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apoorvmote/cdk-examples/4fb551420a2da0ed2e186a133ebf2bb2fb54c38d/nextjs-pipeline/next-client/public/favicon.ico -------------------------------------------------------------------------------- /nextjs-pipeline/next-client/public/vercel.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | -------------------------------------------------------------------------------- /nextjs-pipeline/next-client/styles/Home.module.css: -------------------------------------------------------------------------------- 1 | .container { 2 | min-height: 100vh; 3 | padding: 0 0.5rem; 4 | display: flex; 5 | flex-direction: column; 6 | justify-content: center; 7 | align-items: center; 8 | } 9 | 10 | .main { 11 | padding: 5rem 0; 12 | flex: 1; 13 | display: flex; 14 | flex-direction: column; 15 | justify-content: center; 16 | align-items: center; 17 | } 18 | 19 | .footer { 20 | width: 100%; 21 | height: 100px; 22 | border-top: 1px solid #eaeaea; 23 | display: flex; 24 | justify-content: center; 25 | align-items: center; 26 | } 27 | 28 | .footer img { 29 | margin-left: 0.5rem; 30 | } 31 | 32 | .footer a { 33 | display: flex; 34 | justify-content: center; 35 | align-items: center; 36 | } 37 | 38 | .title a { 39 | color: #0070f3; 40 | text-decoration: none; 41 | } 42 | 43 | .title a:hover, 44 | .title a:focus, 45 | .title a:active { 46 | text-decoration: underline; 47 | } 48 | 49 | .title { 50 | margin: 0; 51 | line-height: 1.15; 52 | font-size: 4rem; 53 | } 54 | 55 | .title, 56 | .description { 57 | text-align: center; 58 | } 59 | 60 | .description { 61 | line-height: 1.5; 62 | font-size: 1.5rem; 63 | } 64 | 65 | .code { 66 | background: #fafafa; 67 | border-radius: 5px; 68 | padding: 0.75rem; 69 | font-size: 1.1rem; 70 | font-family: Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono, 71 | Bitstream Vera Sans Mono, Courier New, monospace; 72 | } 73 | 74 | .grid { 75 | display: flex; 76 | align-items: center; 77 | justify-content: center; 78 | flex-wrap: wrap; 79 | max-width: 800px; 80 | margin-top: 3rem; 81 | } 82 | 83 | .card { 84 | margin: 1rem; 85 | flex-basis: 45%; 86 | padding: 1.5rem; 87 | text-align: left; 88 | color: inherit; 89 | text-decoration: none; 90 | border: 1px solid #eaeaea; 91 | border-radius: 10px; 92 | transition: color 0.15s ease, border-color 0.15s ease; 93 | } 94 | 95 | .card:hover, 96 | .card:focus, 97 | .card:active { 98 | color: #0070f3; 99 | border-color: #0070f3; 100 | } 101 | 102 | .card h3 { 103 | margin: 0 0 1rem 0; 104 | font-size: 1.5rem; 105 | } 106 | 107 | .card p { 108 | margin: 0; 109 | font-size: 1.25rem; 110 | line-height: 1.5; 111 | } 112 | 113 | .logo { 114 | height: 1em; 115 | } 116 | 117 | @media (max-width: 600px) { 118 | .grid { 119 | width: 100%; 120 | flex-direction: column; 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /nextjs-pipeline/next-client/styles/globals.css: -------------------------------------------------------------------------------- 1 | html, 2 | body { 3 | padding: 0; 4 | margin: 0; 5 | font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, 6 | Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif; 7 | } 8 | 9 | a { 10 | color: inherit; 11 | text-decoration: none; 12 | } 13 | 14 | * { 15 | box-sizing: border-box; 16 | } 17 | -------------------------------------------------------------------------------- /password-protect-s3-static-site/.gitignore: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /password-protect-s3-static-site/.npmignore: -------------------------------------------------------------------------------- 1 | *.ts 2 | !*.d.ts 3 | 4 | # CDK asset staging directory 5 | .cdk.staging 6 | cdk.out 7 | -------------------------------------------------------------------------------- /password-protect-s3-static-site/README.md: -------------------------------------------------------------------------------- 1 | # [Basic auth to Password protect s3 website with cloudfront lambda@edge](https://apoorv.blog/password-protect-s3-static-site/) 2 | 3 | [![Read Tutorial](https://badgen.now.sh/badge/Read/Tutorial/purple)](https://apoorv.blog/password-protect-s3-static-site/) 4 | [![MIT License](https://badgen.now.sh/badge/License/MIT/blue)](https://github.com/apoorvmote/cdk-examples/blob/master/License.md) 5 | ![AWS CDK 2.38.1](https://badgen.net/badge/aws-cdk/2.38.1/yellow) 6 | ![Typescript](https://badgen.net/badge/icon/typescript?icon=typescript&label) 7 | 8 | Before deploying changed to production website we deploy changes to preview site and test. -------------------------------------------------------------------------------- /password-protect-s3-static-site/Taskfile.yml: -------------------------------------------------------------------------------- 1 | # https://taskfile.dev 2 | 3 | version: '3' 4 | 5 | tasks: 6 | update: 7 | cmds: 8 | - rm -rf node_modules/ 9 | - npm i 10 | fix: 11 | cmds: 12 | - npm audit fix -------------------------------------------------------------------------------- /password-protect-s3-static-site/bin/password-protect-s3-static-site.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import 'source-map-support/register'; 3 | import * as cdk from 'aws-cdk-lib'; 4 | import { PasswordProtectS3StaticSiteStack } from '../lib/password-protect-s3-static-site-stack'; 5 | import { virginia } from '../lib/variables'; 6 | 7 | const app = new cdk.App(); 8 | new PasswordProtectS3StaticSiteStack(app, 'PasswordProtectS3StaticSiteStack', { env: virginia }); 9 | -------------------------------------------------------------------------------- /password-protect-s3-static-site/cdk.json: -------------------------------------------------------------------------------- 1 | { 2 | "app": "npx ts-node --prefer-ts-exts bin/password-protect-s3-static-site.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-apigateway:usagePlanKeyOrderInsensitiveId": true, 21 | "@aws-cdk/core:stackRelativeExports": true, 22 | "@aws-cdk/aws-rds:lowercaseDbIdentifier": true, 23 | "@aws-cdk/aws-lambda:recognizeVersionProps": true, 24 | "@aws-cdk/aws-cloudfront:defaultSecurityPolicyTLSv1.2_2021": true 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /password-protect-s3-static-site/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | roots: ['/test'], 3 | testMatch: ['**/*.test.ts'], 4 | transform: { 5 | '^.+\\.tsx?$': 'ts-jest' 6 | } 7 | }; 8 | -------------------------------------------------------------------------------- /password-protect-s3-static-site/lambda-fns/basic-auth/Taskfile.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | tasks: 4 | default: 5 | cmds: 6 | - rm deployment.zip -f 7 | - zip deployment.zip index.js -------------------------------------------------------------------------------- /password-protect-s3-static-site/lambda-fns/basic-auth/index.js: -------------------------------------------------------------------------------- 1 | exports.handler = async (event, context, callback) => { 2 | 3 | const request = event.Records[0].cf.request 4 | 5 | const headers = request.headers 6 | 7 | const user = 'my-username' 8 | 9 | const password = 'my-password' 10 | 11 | const authString = 'Basic ' + Buffer.from(user + ':' + password).toString('base64') 12 | 13 | if (typeof headers.authorization === 'undefined' || headers.authorization[0].value !== authString) { 14 | 15 | const response = { 16 | status: '401', 17 | statusDescription: 'Unauthorized', 18 | body: 'Unauthorized', 19 | headers: { 20 | 'www-authenticate': [{key: 'WWW-Authenticate', value:'Basic'}] 21 | } 22 | } 23 | 24 | callback(null, response) 25 | } 26 | 27 | callback(null, request) 28 | } -------------------------------------------------------------------------------- /password-protect-s3-static-site/lib/password-protect-s3-static-site-stack.ts: -------------------------------------------------------------------------------- 1 | import { Stack, StackProps, CfnOutput, RemovalPolicy, Duration } from "aws-cdk-lib" 2 | import { Construct } from "constructs" 3 | import { DnsValidatedCertificate } from 'aws-cdk-lib/aws-certificatemanager'; 4 | import { AllowedMethods, CacheHeaderBehavior, CachePolicy, Distribution, experimental, HttpVersion, LambdaEdgeEventType, OriginProtocolPolicy, PriceClass, SecurityPolicyProtocol, ViewerProtocolPolicy } from 'aws-cdk-lib/aws-cloudfront'; 5 | import { HttpOrigin } from 'aws-cdk-lib/aws-cloudfront-origins'; 6 | import { AnyPrincipal, Effect, PolicyStatement } from 'aws-cdk-lib/aws-iam'; 7 | import { Code, Runtime } from 'aws-cdk-lib/aws-lambda'; 8 | import { ARecord, HostedZone, RecordTarget } from 'aws-cdk-lib/aws-route53'; 9 | import { CloudFrontTarget } from 'aws-cdk-lib/aws-route53-targets'; 10 | import { Bucket } from 'aws-cdk-lib/aws-s3'; 11 | import { hostedZoneId, website_domain, preview_domain, previewSecret, previewBucketWebsiteUrl } from './variables'; 12 | 13 | export class PasswordProtectS3StaticSiteStack extends Stack { 14 | constructor(scope: Construct, id: string, props?: StackProps) { 15 | super(scope, id, props); 16 | 17 | // The code that defines your stack goes here 18 | /////////////////////////////// 19 | // Part 1 20 | const hostedZone = HostedZone.fromHostedZoneAttributes(this, 'HostedZoneWithAttr', { 21 | hostedZoneId: hostedZoneId, 22 | zoneName: website_domain 23 | }) 24 | 25 | const previewCert = new DnsValidatedCertificate(this, 'previewSSL', { 26 | domainName: preview_domain, 27 | hostedZone 28 | }) 29 | 30 | const previewBucket = new Bucket(this, 'previewBucket', { 31 | removalPolicy: RemovalPolicy.DESTROY, 32 | bucketName: preview_domain, 33 | autoDeleteObjects: true, 34 | websiteIndexDocument: 'index.html', 35 | websiteErrorDocument: '404.html' 36 | }) 37 | 38 | new CfnOutput(this, 'previewBucketWebsiteUrl', { 39 | value: previewBucket.bucketWebsiteUrl 40 | }) 41 | /////////////////////////////// 42 | 43 | /////////////////////////////// 44 | // Part 2 45 | previewBucket.addToResourcePolicy(new PolicyStatement({ 46 | sid: 'allow request from cloudfront to s3 website', 47 | effect: Effect.ALLOW, 48 | principals: [new AnyPrincipal()], 49 | actions: ['s3:GetObject'], 50 | resources: [`${previewBucket.bucketArn}/*`], 51 | conditions: { 52 | "StringLike": { 53 | "aws:Referer": [previewSecret] 54 | } 55 | } 56 | })) 57 | 58 | const previewCachePolicy = new CachePolicy(this, 'previewCachePolicy', { 59 | defaultTtl: Duration.minutes(30), 60 | minTtl: Duration.minutes(25), 61 | maxTtl: Duration.minutes(35), 62 | enableAcceptEncodingBrotli: true, 63 | enableAcceptEncodingGzip: true, 64 | headerBehavior: CacheHeaderBehavior.allowList('authorization') 65 | }) 66 | 67 | const edgeAuth = new experimental.EdgeFunction(this, 'edgeAuthFn', { 68 | runtime: Runtime.NODEJS_14_X, 69 | handler: 'index.handler', 70 | code: Code.fromAsset(`${__dirname}/../lambda-fns/basic-auth/deployment.zip`), 71 | memorySize: 128 72 | }) 73 | /////////////////////////////// 74 | 75 | /////////////////////////////// 76 | // Part 3 77 | const previewDistribution = new Distribution(this, 'previewDistribution', { 78 | defaultBehavior: { 79 | origin: new HttpOrigin(previewBucketWebsiteUrl, { 80 | protocolPolicy: OriginProtocolPolicy.HTTP_ONLY, 81 | customHeaders: { 82 | 'Referer': previewSecret 83 | } 84 | }), 85 | edgeLambdas: [{ 86 | functionVersion: edgeAuth.currentVersion, 87 | eventType: LambdaEdgeEventType.VIEWER_REQUEST 88 | }], 89 | allowedMethods: AllowedMethods.ALLOW_GET_HEAD_OPTIONS, 90 | cachePolicy: previewCachePolicy, 91 | compress: true, 92 | viewerProtocolPolicy: ViewerProtocolPolicy.REDIRECT_TO_HTTPS 93 | }, 94 | domainNames: [preview_domain], 95 | certificate: previewCert, 96 | minimumProtocolVersion: SecurityPolicyProtocol.TLS_V1_2_2021, 97 | httpVersion: HttpVersion.HTTP2, 98 | priceClass: PriceClass.PRICE_CLASS_ALL 99 | }) 100 | /////////////////////////////// 101 | 102 | /////////////////////////////// 103 | // Part 4 104 | new ARecord(this, 'aliasForPreview', { 105 | target: RecordTarget.fromAlias(new CloudFrontTarget(previewDistribution)), 106 | zone: hostedZone, 107 | recordName: preview_domain 108 | }) 109 | /////////////////////////////// 110 | 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /password-protect-s3-static-site/lib/variables.ts: -------------------------------------------------------------------------------- 1 | export const hostedZoneId = 'myZoneId' 2 | 3 | export const website_domain = 'example.com' 4 | 5 | export const preview_domain = 'preview.example.com' 6 | 7 | export const websiteCertArn = 'myCertificateArn' 8 | 9 | export const websiteBucketArn = 'arn:aws:s3:::example.com' 10 | 11 | export const hugoRepoArn = 'myRepoArn' 12 | 13 | export const virginia = { account: '012345678901', region: 'us-east-1' } 14 | 15 | export const previewBucketWebsiteUrl = 'preview.example.com.s3-website-us-east-1.amazonaws.com' 16 | 17 | export const previewSecret = 'my-secret-key' -------------------------------------------------------------------------------- /password-protect-s3-static-site/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "password-protect-s3-static-site", 3 | "version": "1.1.0", 4 | "bin": { 5 | "password-protect-s3-static-site": "bin/password-protect-s3-static-site.js" 6 | }, 7 | "scripts": { 8 | "build": "tsc", 9 | "watch": "tsc -w", 10 | "test": "jest", 11 | "cdk": "cdk" 12 | }, 13 | "devDependencies": { 14 | "@types/jest": "^27.5.0", 15 | "@types/node": "10.17.27", 16 | "jest": "^27.5.1", 17 | "ts-jest": "^27.1.4", 18 | "aws-cdk": "2.38.1", 19 | "ts-node": "^10.7.0", 20 | "typescript": "~3.9.7" 21 | }, 22 | "dependencies": { 23 | "aws-cdk-lib": "2.38.1", 24 | "constructs": "^10.0.0", 25 | "source-map-support": "^0.5.21" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /password-protect-s3-static-site/test/password-protect-s3-static-site.test.ts: -------------------------------------------------------------------------------- 1 | import { expect as expectCDK, matchTemplate, MatchStyle } from '@aws-cdk/assert'; 2 | import * as cdk from '@aws-cdk/core'; 3 | import * as PasswordProtectS3StaticSite from '../lib/password-protect-s3-static-site-stack'; 4 | 5 | test('Empty Stack', () => { 6 | const app = new cdk.App(); 7 | // WHEN 8 | const stack = new PasswordProtectS3StaticSite.PasswordProtectS3StaticSiteStack(app, 'MyTestStack'); 9 | // THEN 10 | expectCDK(stack).to(matchTemplate({ 11 | "Resources": {} 12 | }, MatchStyle.EXACT)) 13 | }); 14 | -------------------------------------------------------------------------------- /password-protect-s3-static-site/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2018", 4 | "module": "commonjs", 5 | "lib": ["es2018"], 6 | "declaration": true, 7 | "strict": true, 8 | "noImplicitAny": true, 9 | "strictNullChecks": true, 10 | "noImplicitThis": true, 11 | "alwaysStrict": true, 12 | "noUnusedLocals": false, 13 | "noUnusedParameters": false, 14 | "noImplicitReturns": true, 15 | "noFallthroughCasesInSwitch": false, 16 | "inlineSourceMap": true, 17 | "inlineSources": true, 18 | "experimentalDecorators": true, 19 | "strictPropertyInitialization": false, 20 | "typeRoots": ["./node_modules/@types"] 21 | }, 22 | "exclude": ["cdk.out"] 23 | } 24 | -------------------------------------------------------------------------------- /react-pipeline/Readme.md: -------------------------------------------------------------------------------- 1 | # [Build and deploy react app with Code Pipeline and Cloudfront](https://apoorv.blog/deploy-reactjs-cloudfront-codepipeline-cdk/) 2 | 3 | [![Read Tutorial](https://badgen.now.sh/badge/Read/Tutorial/purple)](https://apoorv.blog/deploy-reactjs-cloudfront-codepipeline-cdk/) 4 | [![MIT License](https://badgen.now.sh/badge/License/MIT/blue)](https://github.com/apoorvmote/cdk-examples/blob/master/License.md) 5 | ![AWS CDK 2.38.1](https://badgen.net/badge/aws-cdk/2.38.1/yellow) 6 | ![Typescript](https://badgen.net/badge/icon/typescript?icon=typescript&label) 7 | 8 | **Very important** make sure to deploy stacks in Parts as assigned in the code. 9 | 10 | In this example React app is stored in AWS CodeCommit, built with AWS Codepipeline and deployed to AWS S3 and AWS Cloudfront -------------------------------------------------------------------------------- /react-pipeline/backend-infra/.gitignore: -------------------------------------------------------------------------------- 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 | 10 | # Parcel default cache directory 11 | .parcel-cache 12 | -------------------------------------------------------------------------------- /react-pipeline/backend-infra/.npmignore: -------------------------------------------------------------------------------- 1 | *.ts 2 | !*.d.ts 3 | 4 | # CDK asset staging directory 5 | .cdk.staging 6 | cdk.out 7 | -------------------------------------------------------------------------------- /react-pipeline/backend-infra/README.md: -------------------------------------------------------------------------------- 1 | # Welcome to your CDK TypeScript project! 2 | 3 | This is a blank project for TypeScript development with CDK. 4 | 5 | The `cdk.json` file tells the CDK Toolkit how to execute your app. 6 | 7 | ## Useful commands 8 | 9 | * `npm run build` compile typescript to js 10 | * `npm run watch` watch for changes and compile 11 | * `npm run test` perform the jest unit tests 12 | * `cdk deploy` deploy this stack to your default AWS account/region 13 | * `cdk diff` compare deployed stack with current state 14 | * `cdk synth` emits the synthesized CloudFormation template 15 | -------------------------------------------------------------------------------- /react-pipeline/backend-infra/Taskfile.yml: -------------------------------------------------------------------------------- 1 | # https://taskfile.dev 2 | 3 | version: '3' 4 | 5 | tasks: 6 | update: 7 | cmds: 8 | - rm -rf node_modules/ 9 | - npm i 10 | fix: 11 | cmds: 12 | - npm audit fix -------------------------------------------------------------------------------- /react-pipeline/backend-infra/bin/backend-infra.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import 'source-map-support/register'; 3 | import * as cdk from 'aws-cdk-lib'; 4 | import { CertificateStack } from '../lib/certificate'; 5 | import { S3CloudfrontStack } from '../lib/s3cloudfront'; 6 | import { CodePipelineStack } from '../lib/code-pipeline'; 7 | import { virginia } from '../lib/variables'; 8 | // import { BackendInfraStack } from '../lib/backend-infra-stack'; 9 | 10 | const app = new cdk.App(); 11 | // new BackendInfraStack(app, 'BackendInfraStack'); 12 | 13 | new CertificateStack(app, 'certificate-avDFA2jV6u6gUeUz', { env: virginia }) 14 | 15 | new S3CloudfrontStack(app, 's3cloudfront-CggFnHGGvbCR4Nww', { env: virginia }) 16 | 17 | new CodePipelineStack(app, 'codepipeline-VdGaVYQZ7XSa3VWF', { env: virginia }) -------------------------------------------------------------------------------- /react-pipeline/backend-infra/cdk.json: -------------------------------------------------------------------------------- 1 | { 2 | "app": "npx ts-node --prefer-ts-exts bin/backend-infra.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-apigateway:usagePlanKeyOrderInsensitiveId": true, 21 | "@aws-cdk/core:stackRelativeExports": true, 22 | "@aws-cdk/aws-rds:lowercaseDbIdentifier": true, 23 | "@aws-cdk/aws-lambda:recognizeVersionProps": true, 24 | "@aws-cdk/aws-cloudfront:defaultSecurityPolicyTLSv1.2_2021": true 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /react-pipeline/backend-infra/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | roots: ['/test'], 3 | testMatch: ['**/*.test.ts'], 4 | transform: { 5 | '^.+\\.tsx?$': 'ts-jest' 6 | } 7 | }; 8 | -------------------------------------------------------------------------------- /react-pipeline/backend-infra/lib/backend-infra-stack.ts: -------------------------------------------------------------------------------- 1 | import { Stack, StackProps } from "aws-cdk-lib" 2 | import { Construct } from "constructs" 3 | 4 | export class BackendInfraStack extends Stack { 5 | constructor(scope: Construct, id: string, props?: StackProps) { 6 | super(scope, id, props); 7 | 8 | // The code that defines your stack goes here 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /react-pipeline/backend-infra/lib/certificate.ts: -------------------------------------------------------------------------------- 1 | import { Stack, StackProps, CfnOutput } from "aws-cdk-lib" 2 | import { Construct } from "constructs" 3 | import { DnsValidatedCertificate } from 'aws-cdk-lib/aws-certificatemanager'; 4 | import { HostedZone } from 'aws-cdk-lib/aws-route53'; 5 | import { hostedZoneId, website_domain } from './variables'; 6 | 7 | export class CertificateStack extends Stack { 8 | constructor(scope: Construct, id: string, props?: StackProps) { 9 | super(scope, id, props); 10 | /////////////////////////////// 11 | // Part 1 12 | const hostedZone = HostedZone.fromHostedZoneAttributes(this, 'HostedZoneWithAttrs', { 13 | hostedZoneId, 14 | zoneName: website_domain 15 | }) 16 | 17 | const websiteCert = new DnsValidatedCertificate(this, 'WebsiteSSL', { 18 | domainName: website_domain, 19 | hostedZone 20 | }) 21 | 22 | new CfnOutput(this, 'WebsiteCertArn', { 23 | value: websiteCert.certificateArn 24 | }) 25 | /////////////////////////////// 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /react-pipeline/backend-infra/lib/code-pipeline.ts: -------------------------------------------------------------------------------- 1 | import { Stack, StackProps, RemovalPolicy } from "aws-cdk-lib" 2 | import { Construct } from "constructs" 3 | import { BuildSpec, ComputeType, LinuxBuildImage, PipelineProject } from 'aws-cdk-lib/aws-codebuild'; 4 | import { Repository } from 'aws-cdk-lib/aws-codecommit'; 5 | import { Artifact, Pipeline } from 'aws-cdk-lib/aws-codepipeline'; 6 | import { CodeBuildAction, CodeCommitSourceAction, S3DeployAction } from 'aws-cdk-lib/aws-codepipeline-actions'; 7 | import { Bucket } from 'aws-cdk-lib/aws-s3'; 8 | import { reactRepoArn, websiteBucketArn } from './variables'; 9 | 10 | export class CodePipelineStack extends Stack { 11 | constructor(scope: Construct, id: string, props?: StackProps) { 12 | super(scope, id, props); 13 | 14 | /////////////////////////////// 15 | // Part 7 16 | const websiteBucket = Bucket.fromBucketArn(this, 'websiteBucket', websiteBucketArn) 17 | 18 | const reactBuildProject = new PipelineProject(this, 'reactBuild', { 19 | buildSpec: BuildSpec.fromSourceFilename('buildspec.yml'), 20 | environment: { 21 | buildImage: LinuxBuildImage.STANDARD_5_0, 22 | computeType: ComputeType.SMALL 23 | } 24 | }) 25 | 26 | const artifactBucket = new Bucket(this, 'reactPipelineArtifactBucket', { 27 | bucketName: 'react-pipeline-artifact-bucket', 28 | removalPolicy: RemovalPolicy.DESTROY 29 | }) 30 | 31 | const gitOutput = new Artifact('reactRepoLatestMaster') 32 | 33 | const buildOutput = new Artifact('reactBuildOutput') 34 | 35 | new Pipeline(this, 'reactPipeline', { 36 | artifactBucket, 37 | pipelineName: 'examplePipeline', 38 | stages: [ 39 | { 40 | stageName: 'SourceCode', 41 | actions: [ 42 | new CodeCommitSourceAction({ 43 | actionName: 'readLatestMasterCommit', 44 | branch: 'main', 45 | output: gitOutput, 46 | repository: Repository.fromRepositoryArn(this, 'reactGitRepo', reactRepoArn) 47 | }) 48 | ] 49 | }, 50 | { 51 | stageName: 'Build', 52 | actions: [ 53 | new CodeBuildAction({ 54 | actionName: 'buildReactApp', 55 | input: gitOutput, 56 | outputs: [buildOutput], 57 | project: reactBuildProject 58 | }) 59 | ] 60 | }, 61 | { 62 | stageName: 'Deploy', 63 | actions: [ 64 | new S3DeployAction({ 65 | actionName: 'DeployReactApp', 66 | input: buildOutput, 67 | bucket: websiteBucket 68 | }) 69 | ] 70 | } 71 | ] 72 | }) 73 | /////////////////////////////// 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /react-pipeline/backend-infra/lib/s3cloudfront.ts: -------------------------------------------------------------------------------- 1 | import { Stack, StackProps, CfnOutput, RemovalPolicy, Duration } from "aws-cdk-lib" 2 | import { Construct } from "constructs" 3 | import { Certificate } from 'aws-cdk-lib/aws-certificatemanager'; 4 | import { AllowedMethods, CachePolicy, CloudFrontWebDistribution, Distribution, HttpVersion, OriginAccessIdentity, PriceClass, SecurityPolicyProtocol, ViewerCertificate, ViewerProtocolPolicy } from 'aws-cdk-lib/aws-cloudfront'; 5 | import { S3Origin } from 'aws-cdk-lib/aws-cloudfront-origins'; 6 | import { Repository } from 'aws-cdk-lib/aws-codecommit'; 7 | import { ARecord, HostedZone, RecordTarget } from 'aws-cdk-lib/aws-route53'; 8 | import { HttpsRedirect } from 'aws-cdk-lib/aws-route53-patterns'; 9 | import { CloudFrontTarget } from 'aws-cdk-lib/aws-route53-targets'; 10 | import { Bucket } from 'aws-cdk-lib/aws-s3'; 11 | import { hostedZoneId, websiteCertArn, website_domain } from './variables'; 12 | 13 | export class S3CloudfrontStack extends Stack { 14 | constructor(scope: Construct, id: string, props?: StackProps) { 15 | super(scope, id, props); 16 | 17 | /////////////////////////////// 18 | // Part 2 19 | const bucket = new Bucket(this, 'websiteBucket', { 20 | removalPolicy: RemovalPolicy.DESTROY, 21 | bucketName: website_domain, 22 | autoDeleteObjects: true 23 | }) 24 | 25 | new CfnOutput(this, 'websiteBucketArn', { 26 | value: bucket.bucketArn 27 | }) 28 | /////////////////////////////// 29 | 30 | /////////////////////////////// 31 | // Part 3 32 | const certificate = Certificate.fromCertificateArn(this, 'websiteCert', websiteCertArn) 33 | 34 | const cachePolicy = new CachePolicy(this, 'examplePolicy', { 35 | defaultTtl: Duration.hours(24), 36 | minTtl: Duration.hours(24), 37 | maxTtl: Duration.hours(24), 38 | enableAcceptEncodingGzip: true, 39 | enableAcceptEncodingBrotli: true 40 | }) 41 | 42 | const distribution = new Distribution(this, 'exampleDistribution', { 43 | defaultBehavior: { 44 | origin: new S3Origin(bucket), 45 | allowedMethods: AllowedMethods.ALLOW_GET_HEAD_OPTIONS, 46 | cachePolicy, 47 | compress: true, 48 | viewerProtocolPolicy: ViewerProtocolPolicy.REDIRECT_TO_HTTPS 49 | }, 50 | domainNames: [website_domain], 51 | certificate, 52 | minimumProtocolVersion: SecurityPolicyProtocol.TLS_V1_2_2021, 53 | defaultRootObject: 'index.html', 54 | enableIpv6: true, 55 | enabled: true, 56 | httpVersion: HttpVersion.HTTP2, 57 | priceClass: PriceClass.PRICE_CLASS_ALL 58 | }) 59 | /////////////////////////////// 60 | 61 | /////////////////////////////// 62 | // Part 4 63 | const hostedZone = HostedZone.fromHostedZoneAttributes(this, 'hostedZoneWithAttrs', { 64 | hostedZoneId, 65 | zoneName: website_domain 66 | }) 67 | 68 | new ARecord(this, 'aliasForCloudfront', { 69 | target: RecordTarget.fromAlias(new CloudFrontTarget(distribution)), 70 | zone: hostedZone, 71 | recordName: website_domain 72 | }) 73 | /////////////////////////////// 74 | 75 | /////////////////////////////// 76 | // Part 5 77 | new HttpsRedirect(this, 'wwwToNonWww', { 78 | recordNames: ['www.example.com'], 79 | targetDomain: website_domain, 80 | zone:hostedZone 81 | }) 82 | 83 | const repo = new Repository(this, 'reactSourceCode', { 84 | repositoryName: 'example', 85 | description: `react repo for ${website_domain}` 86 | }) 87 | 88 | new CfnOutput(this, 'reactRepoArn', { 89 | value: repo.repositoryArn 90 | }) 91 | /////////////////////////////// 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /react-pipeline/backend-infra/lib/variables.ts: -------------------------------------------------------------------------------- 1 | export const hostedZoneId = 'myZoneId' 2 | 3 | export const website_domain = 'example.com' 4 | 5 | export const websiteCertArn = 'myCertificateArn' 6 | 7 | export const websiteBucketArn = 'arn:aws:s3:::example.com' 8 | 9 | export const reactRepoArn = 'myRepoArn' 10 | 11 | export const virginia = { account: '012345678901', region: 'us-east-1' } -------------------------------------------------------------------------------- /react-pipeline/backend-infra/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "backend-infra", 3 | "version": "1.1.0", 4 | "bin": { 5 | "backend-infra": "bin/backend-infra.js" 6 | }, 7 | "scripts": { 8 | "build": "tsc", 9 | "watch": "tsc -w", 10 | "test": "jest", 11 | "cdk": "cdk" 12 | }, 13 | "devDependencies": { 14 | "@types/jest": "^27.5.0", 15 | "@types/node": "10.17.27", 16 | "jest": "^27.5.1", 17 | "ts-jest": "^27.1.4", 18 | "aws-cdk": "2.38.1", 19 | "ts-node": "^10.7.0", 20 | "typescript": "~3.9.7" 21 | }, 22 | "dependencies": { 23 | "aws-cdk-lib": "2.38.1", 24 | "constructs": "^10.0.0", 25 | "source-map-support": "^0.5.21" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /react-pipeline/backend-infra/test/backend-infra.test.ts: -------------------------------------------------------------------------------- 1 | import { expect as expectCDK, matchTemplate, MatchStyle } from '@aws-cdk/assert'; 2 | import * as cdk from '@aws-cdk/core'; 3 | import * as BackendInfra from '../lib/backend-infra-stack'; 4 | 5 | test('Empty Stack', () => { 6 | const app = new cdk.App(); 7 | // WHEN 8 | const stack = new BackendInfra.BackendInfraStack(app, 'MyTestStack'); 9 | // THEN 10 | expectCDK(stack).to(matchTemplate({ 11 | "Resources": {} 12 | }, MatchStyle.EXACT)) 13 | }); 14 | -------------------------------------------------------------------------------- /react-pipeline/backend-infra/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2018", 4 | "module": "commonjs", 5 | "lib": ["es2018"], 6 | "declaration": true, 7 | "strict": true, 8 | "noImplicitAny": true, 9 | "strictNullChecks": true, 10 | "noImplicitThis": true, 11 | "alwaysStrict": true, 12 | "noUnusedLocals": false, 13 | "noUnusedParameters": false, 14 | "noImplicitReturns": true, 15 | "noFallthroughCasesInSwitch": false, 16 | "inlineSourceMap": true, 17 | "inlineSources": true, 18 | "experimentalDecorators": true, 19 | "strictPropertyInitialization": false, 20 | "typeRoots": ["./node_modules/@types"] 21 | }, 22 | "exclude": ["cdk.out"] 23 | } 24 | -------------------------------------------------------------------------------- /react-pipeline/react-client/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /react-pipeline/react-client/README.md: -------------------------------------------------------------------------------- 1 | # Getting Started with Create React App 2 | 3 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). 4 | 5 | ## Available Scripts 6 | 7 | In the project directory, you can run: 8 | 9 | ### `npm start` 10 | 11 | Runs the app in the development mode.\ 12 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser. 13 | 14 | The page will reload if you make edits.\ 15 | You will also see any lint errors in the console. 16 | 17 | ### `npm test` 18 | 19 | Launches the test runner in the interactive watch mode.\ 20 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. 21 | 22 | ### `npm run build` 23 | 24 | Builds the app for production to the `build` folder.\ 25 | It correctly bundles React in production mode and optimizes the build for the best performance. 26 | 27 | The build is minified and the filenames include the hashes.\ 28 | Your app is ready to be deployed! 29 | 30 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. 31 | 32 | ### `npm run eject` 33 | 34 | **Note: this is a one-way operation. Once you `eject`, you can’t go back!** 35 | 36 | If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project. 37 | 38 | Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own. 39 | 40 | You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it. 41 | 42 | ## Learn More 43 | 44 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started). 45 | 46 | To learn React, check out the [React documentation](https://reactjs.org/). 47 | -------------------------------------------------------------------------------- /react-pipeline/react-client/buildspec.yml: -------------------------------------------------------------------------------- 1 | version: 0.2 2 | 3 | phases: 4 | install: 5 | runtime-versions: 6 | nodejs: 14.x 7 | commands: 8 | - echo Installing npm packages 9 | - npm install 10 | - npm update 11 | build: 12 | commands: 13 | - echo Building react app 14 | - npm run build 15 | - echo Built react app on `date` 16 | 17 | artifacts: 18 | base-directory: ./build 19 | files: 20 | - '**/*' -------------------------------------------------------------------------------- /react-pipeline/react-client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-client", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^5.11.6", 7 | "@testing-library/react": "^11.1.2", 8 | "@testing-library/user-event": "^12.2.2", 9 | "@types/jest": "^26.0.15", 10 | "@types/node": "^12.19.4", 11 | "@types/react": "^16.9.56", 12 | "@types/react-dom": "^16.9.9", 13 | "react": "^17.0.1", 14 | "react-dom": "^17.0.1", 15 | "react-scripts": "4.0.0", 16 | "typescript": "^4.0.5", 17 | "web-vitals": "^0.2.4" 18 | }, 19 | "scripts": { 20 | "start": "react-scripts start", 21 | "build": "react-scripts build", 22 | "test": "react-scripts test", 23 | "eject": "react-scripts eject" 24 | }, 25 | "eslintConfig": { 26 | "extends": [ 27 | "react-app", 28 | "react-app/jest" 29 | ] 30 | }, 31 | "browserslist": { 32 | "production": [ 33 | ">0.2%", 34 | "not dead", 35 | "not op_mini all" 36 | ], 37 | "development": [ 38 | "last 1 chrome version", 39 | "last 1 firefox version", 40 | "last 1 safari version" 41 | ] 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /react-pipeline/react-client/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apoorvmote/cdk-examples/4fb551420a2da0ed2e186a133ebf2bb2fb54c38d/react-pipeline/react-client/public/favicon.ico -------------------------------------------------------------------------------- /react-pipeline/react-client/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | React App 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /react-pipeline/react-client/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apoorvmote/cdk-examples/4fb551420a2da0ed2e186a133ebf2bb2fb54c38d/react-pipeline/react-client/public/logo192.png -------------------------------------------------------------------------------- /react-pipeline/react-client/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apoorvmote/cdk-examples/4fb551420a2da0ed2e186a133ebf2bb2fb54c38d/react-pipeline/react-client/public/logo512.png -------------------------------------------------------------------------------- /react-pipeline/react-client/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /react-pipeline/react-client/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /react-pipeline/react-client/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | height: 40vmin; 7 | pointer-events: none; 8 | } 9 | 10 | @media (prefers-reduced-motion: no-preference) { 11 | .App-logo { 12 | animation: App-logo-spin infinite 20s linear; 13 | } 14 | } 15 | 16 | .App-header { 17 | background-color: #282c34; 18 | min-height: 100vh; 19 | display: flex; 20 | flex-direction: column; 21 | align-items: center; 22 | justify-content: center; 23 | font-size: calc(10px + 2vmin); 24 | color: white; 25 | } 26 | 27 | .App-link { 28 | color: #61dafb; 29 | } 30 | 31 | @keyframes App-logo-spin { 32 | from { 33 | transform: rotate(0deg); 34 | } 35 | to { 36 | transform: rotate(360deg); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /react-pipeline/react-client/src/App.test.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { render, screen } from '@testing-library/react'; 3 | import App from './App'; 4 | 5 | test('renders learn react link', () => { 6 | render(); 7 | const linkElement = screen.getByText(/learn react/i); 8 | expect(linkElement).toBeInTheDocument(); 9 | }); 10 | -------------------------------------------------------------------------------- /react-pipeline/react-client/src/App.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import logo from './logo.svg'; 3 | import './App.css'; 4 | 5 | function App() { 6 | return ( 7 |
8 |
9 | logo 10 |

11 | Edit src/App.tsx and save to reload. 12 |

13 | 19 | Learn React 20 | 21 |
22 |
23 | ); 24 | } 25 | 26 | export default App; 27 | -------------------------------------------------------------------------------- /react-pipeline/react-client/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 | monospace; 13 | } 14 | -------------------------------------------------------------------------------- /react-pipeline/react-client/src/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.css'; 4 | import App from './App'; 5 | import reportWebVitals from './reportWebVitals'; 6 | 7 | ReactDOM.render( 8 | 9 | 10 | , 11 | document.getElementById('root') 12 | ); 13 | 14 | // If you want to start measuring performance in your app, pass a function 15 | // to log results (for example: reportWebVitals(console.log)) 16 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals 17 | reportWebVitals(); 18 | -------------------------------------------------------------------------------- /react-pipeline/react-client/src/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /react-pipeline/react-client/src/react-app-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /react-pipeline/react-client/src/reportWebVitals.ts: -------------------------------------------------------------------------------- 1 | import { ReportHandler } from 'web-vitals'; 2 | 3 | const reportWebVitals = (onPerfEntry?: ReportHandler) => { 4 | if (onPerfEntry && onPerfEntry instanceof Function) { 5 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 6 | getCLS(onPerfEntry); 7 | getFID(onPerfEntry); 8 | getFCP(onPerfEntry); 9 | getLCP(onPerfEntry); 10 | getTTFB(onPerfEntry); 11 | }); 12 | } 13 | } 14 | 15 | export default reportWebVitals; 16 | -------------------------------------------------------------------------------- /react-pipeline/react-client/src/setupTests.ts: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom'; 6 | -------------------------------------------------------------------------------- /react-pipeline/react-client/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": [ 5 | "dom", 6 | "dom.iterable", 7 | "esnext" 8 | ], 9 | "allowJs": true, 10 | "skipLibCheck": true, 11 | "esModuleInterop": true, 12 | "allowSyntheticDefaultImports": true, 13 | "strict": true, 14 | "forceConsistentCasingInFileNames": true, 15 | "noFallthroughCasesInSwitch": true, 16 | "module": "esnext", 17 | "moduleResolution": "node", 18 | "resolveJsonModule": true, 19 | "isolatedModules": true, 20 | "noEmit": true, 21 | "jsx": "react" 22 | }, 23 | "include": [ 24 | "src" 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /ts-lambda/.gitignore: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /ts-lambda/.npmignore: -------------------------------------------------------------------------------- 1 | *.ts 2 | !*.d.ts 3 | 4 | # CDK asset staging directory 5 | .cdk.staging 6 | cdk.out 7 | -------------------------------------------------------------------------------- /ts-lambda/README.md: -------------------------------------------------------------------------------- 1 | # [Typescript Lambda](https://apoorv.blog/typescript-lambda-cdk/) 2 | 3 | [![Read Tutorial](https://badgen.now.sh/badge/Read/Tutorial/purple)](https://apoorv.blog/typescript-lambda-cdk/) 4 | [![MIT License](https://badgen.now.sh/badge/License/MIT/blue)](https://github.com/apoorvmote/cdk-examples/blob/master/License.md) 5 | ![AWS CDK 2.38.1](https://badgen.net/badge/aws-cdk/2.38.1/yellow) 6 | ![Typescript](https://badgen.net/badge/icon/typescript?icon=typescript&label) 7 | 8 | Lambda function written in typescript. It is compiled and build to javascript and deployed with CDK. -------------------------------------------------------------------------------- /ts-lambda/Taskfile.yml: -------------------------------------------------------------------------------- 1 | # https://taskfile.dev 2 | 3 | version: '3' 4 | 5 | tasks: 6 | update: 7 | cmds: 8 | - rm -rf node_modules/ 9 | - npm i 10 | fix: 11 | cmds: 12 | - npm audit fix -------------------------------------------------------------------------------- /ts-lambda/bin/ts-lambda.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import 'source-map-support/register'; 3 | import * as cdk from 'aws-cdk-lib'; 4 | import { TsLambdaStack } from '../lib/ts-lambda-stack'; 5 | 6 | const app = new cdk.App(); 7 | new TsLambdaStack(app, 'TsLambdaStack'); 8 | -------------------------------------------------------------------------------- /ts-lambda/cdk.json: -------------------------------------------------------------------------------- 1 | { 2 | "app": "npx ts-node --prefer-ts-exts bin/ts-lambda.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-apigateway:usagePlanKeyOrderInsensitiveId": true, 21 | "@aws-cdk/core:stackRelativeExports": true, 22 | "@aws-cdk/aws-rds:lowercaseDbIdentifier": true, 23 | "@aws-cdk/aws-lambda:recognizeVersionProps": true, 24 | "@aws-cdk/aws-cloudfront:defaultSecurityPolicyTLSv1.2_2021": true 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /ts-lambda/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | roots: ['/test'], 3 | testMatch: ['**/*.test.ts'], 4 | transform: { 5 | '^.+\\.tsx?$': 'ts-jest' 6 | } 7 | }; 8 | -------------------------------------------------------------------------------- /ts-lambda/lambda-fns/hello-world/index.ts: -------------------------------------------------------------------------------- 1 | import { APIGatewayProxyEventV2, APIGatewayProxyResultV2 } from 'aws-lambda' 2 | 3 | export async function myFunction(event: APIGatewayProxyEventV2): Promise { 4 | 5 | return { 6 | statusCode: 200, 7 | body: JSON.stringify({ message: 'hello from ts lambda' }) 8 | } 9 | } -------------------------------------------------------------------------------- /ts-lambda/lambda-fns/hello-world/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hello-world", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@types/aws-lambda": { 8 | "version": "8.10.68", 9 | "resolved": "https://registry.npmjs.org/@types/aws-lambda/-/aws-lambda-8.10.68.tgz", 10 | "integrity": "sha512-0/0ghix1WXU8xJxvRx/VtmAesLR6+dPFWrNhQ90Pq6oOYN3x+oRVPnjjYNOIRuEd7fmKovl7s6ILCXMnbDr4Mg==", 11 | "dev": true 12 | }, 13 | "@types/node": { 14 | "version": "14.14.19", 15 | "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.19.tgz", 16 | "integrity": "sha512-4nhBPStMK04rruRVtVc6cDqhu7S9GZai0fpXgPXrFpcPX6Xul8xnrjSdGB4KPBVYG/R5+fXWdCM8qBoiULWGPQ==", 17 | "dev": true 18 | }, 19 | "typescript": { 20 | "version": "4.1.3", 21 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.3.tgz", 22 | "integrity": "sha512-B3ZIOf1IKeH2ixgHhj6la6xdwR9QrLC5d1VKeCSY4tvkqhF2eqd9O7txNlS0PO3GrBAFIdr3L1ndNwteUbZLYg==", 23 | "dev": true 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /ts-lambda/lambda-fns/hello-world/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hello-world", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "devDependencies": { 13 | "@types/aws-lambda": "^8.10.68", 14 | "@types/node": "^14.14.19", 15 | "typescript": "^4.1.3" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /ts-lambda/lib/ts-lambda-stack.ts: -------------------------------------------------------------------------------- 1 | import { Stack, StackProps } from "aws-cdk-lib" 2 | import { Construct } from "constructs" 3 | import { Architecture, Runtime } from 'aws-cdk-lib/aws-lambda'; 4 | import { NodejsFunction } from 'aws-cdk-lib/aws-lambda-nodejs'; 5 | 6 | export class TsLambdaStack extends Stack { 7 | constructor(scope: Construct, id: string, props?: StackProps) { 8 | super(scope, id, props); 9 | 10 | // The code that defines your stack goes here 11 | 12 | new NodejsFunction(this, 'helloWorldFn', { 13 | runtime: Runtime.NODEJS_16_X, 14 | entry: `${__dirname}/../lambda-fns/hello-world/index.ts`, 15 | handler: 'myFunction', 16 | memorySize: 128, 17 | architecture: Architecture.ARM_64, 18 | bundling: { 19 | minify: true, 20 | // tsconfig: `${__dirname}/../lambda-fns/hello-world/tsconfig.json` // if you want to override defaults 21 | } 22 | }) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /ts-lambda/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ts-lambda", 3 | "version": "1.1.0", 4 | "bin": { 5 | "ts-lambda": "bin/ts-lambda.js" 6 | }, 7 | "scripts": { 8 | "build": "tsc", 9 | "watch": "tsc -w", 10 | "test": "jest", 11 | "cdk": "cdk" 12 | }, 13 | "devDependencies": { 14 | "@types/jest": "^27.5.0", 15 | "@types/node": "10.17.27", 16 | "jest": "^27.5.1", 17 | "ts-jest": "^27.1.4", 18 | "aws-cdk": "2.38.1", 19 | "ts-node": "^10.7.0", 20 | "typescript": "~3.9.7" 21 | }, 22 | "dependencies": { 23 | "aws-cdk-lib": "2.38.1", 24 | "constructs": "^10.0.0", 25 | "esbuild": "^0.15.5", 26 | "source-map-support": "^0.5.21" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /ts-lambda/test/ts-lambda.test.ts: -------------------------------------------------------------------------------- 1 | import { expect as expectCDK, matchTemplate, MatchStyle } from '@aws-cdk/assert'; 2 | import * as cdk from '@aws-cdk/core'; 3 | import * as TsLambda from '../lib/ts-lambda-stack'; 4 | 5 | test('Empty Stack', () => { 6 | const app = new cdk.App(); 7 | // WHEN 8 | const stack = new TsLambda.TsLambdaStack(app, 'MyTestStack'); 9 | // THEN 10 | expectCDK(stack).to(matchTemplate({ 11 | "Resources": {} 12 | }, MatchStyle.EXACT)) 13 | }); 14 | -------------------------------------------------------------------------------- /ts-lambda/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2018", 4 | "module": "commonjs", 5 | "lib": ["es2018"], 6 | "declaration": true, 7 | "strict": true, 8 | "noImplicitAny": true, 9 | "strictNullChecks": true, 10 | "noImplicitThis": true, 11 | "alwaysStrict": true, 12 | "noUnusedLocals": false, 13 | "noUnusedParameters": false, 14 | "noImplicitReturns": true, 15 | "noFallthroughCasesInSwitch": false, 16 | "inlineSourceMap": true, 17 | "inlineSources": true, 18 | "experimentalDecorators": true, 19 | "strictPropertyInitialization": false, 20 | "typeRoots": ["./node_modules/@types"] 21 | }, 22 | "exclude": ["cdk.out"] 23 | } 24 | --------------------------------------------------------------------------------