├── .gitignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── SQS ├── .gitignore ├── README.md ├── src │ ├── .npmignore │ ├── app.js │ ├── package.json │ └── tests │ │ └── unit │ │ └── test-handler.js └── template.yaml ├── api-enhanced-observability-variables ├── README.md ├── src │ └── app.js └── template.yaml ├── appconfig-lambda-extensions ├── README.md ├── src │ ├── app.js │ ├── package-lock.json │ └── package.json └── template.yaml ├── appsync-singletable ├── cdk │ ├── .gitignore │ ├── .npmignore │ ├── README.md │ ├── bin │ │ └── appsync-cdk.ts │ ├── cdk.json │ ├── jest.config.js │ ├── lib │ │ ├── appsync-cdk-stack.ts │ │ └── schema.graphql │ ├── package-lock.json │ ├── package.json │ ├── test │ │ └── appsync-cdk.test.ts │ └── tsconfig.json └── sam │ ├── .gitignore │ ├── README.md │ ├── __init__.py │ ├── events │ └── event.json │ ├── hello_world │ ├── __init__.py │ ├── app.py │ └── requirements.txt │ ├── template.yaml │ └── tests │ ├── __init__.py │ ├── integration │ ├── __init__.py │ └── test_api_gateway.py │ ├── requirements.txt │ └── unit │ ├── __init__.py │ └── test_handler.py ├── cognito ├── README.md ├── src │ └── app.js └── template.yaml ├── custom-domains ├── README.md ├── both-declared │ └── template.yaml ├── both-implied │ └── template.yaml ├── http │ └── template.yaml └── rest │ └── template.yaml ├── dotnet-api ├── .gitignore ├── .stackery-config.yaml ├── src │ └── SaveUser │ │ ├── .gitignore │ │ ├── .stackery-config.yaml │ │ ├── Function.cs │ │ ├── Function.csproj │ │ └── README.md └── template.yaml ├── eventbridge-dlq-retry ├── README.md ├── api.yaml ├── src │ └── app.js └── template.yaml ├── eventbridge ├── README.md ├── events │ ├── endpoint.json │ ├── sentiment.json │ └── translate.json ├── locals.json ├── src │ ├── endpoint.js │ ├── package.json │ ├── sentiment.js │ └── translate.js └── template.yaml ├── go-al2 ├── README.md ├── hello-world │ ├── Makefile │ ├── main.go │ └── main_test.go └── template.yaml ├── governance ├── 1-initial-setup │ └── template.yaml ├── 2-managed-rules │ ├── src │ │ └── app.js │ └── template.yaml ├── 3-custom-rules │ ├── example-events │ │ ├── 1-config-event.json │ │ ├── 2-invoking-rule.json │ │ ├── 3-rule-parameters.json │ │ └── 4-configuration.json │ ├── src │ │ └── generic-by-params.js │ └── template.yaml ├── 4-reusable-setup │ └── template.yaml ├── 5-reusable-setup-full │ └── template.yaml └── README.md ├── http-api-direct-integration ├── README.md ├── api.yaml ├── kinesis.json ├── src │ └── app.js └── template.yaml ├── http-api-integrations-blog-example ├── README.md ├── api.yaml ├── src │ └── app.js └── template.yaml ├── http-api-logging ├── README.md ├── src │ └── app.js └── template.yaml ├── http-api ├── README.md ├── src │ ├── app.js │ └── package.json └── template.yaml ├── kinesis-firehose ├── README.md ├── src │ ├── count.js │ ├── package.json │ └── process.js └── template.yaml ├── lambda-layers ├── README.md ├── demo-app │ ├── .gitignore │ ├── README.md │ ├── events │ │ └── event.json │ ├── hello-world │ │ ├── .npmignore │ │ ├── app.js │ │ ├── package.json │ │ └── tests │ │ │ └── unit │ │ │ └── test-handler.js │ ├── layer │ │ └── package.json │ └── template.yaml └── layers-repo │ ├── node-base │ ├── package-lock.json │ └── package.json │ ├── node-utilities │ └── package.json │ ├── python-base │ └── requirements.txt │ └── template.yaml ├── multi-level-mapping ├── README.md ├── admin.yaml ├── dadjokes.yaml ├── reportingv1.yaml ├── reportingv2.yaml ├── src │ ├── admin │ │ ├── app.js │ │ └── package.json │ ├── global-reportingv1 │ │ ├── app.js │ │ └── package.json │ ├── global-reportingv2 │ │ ├── app.js │ │ └── package.json │ └── regional-reporting │ │ ├── app.js │ │ └── package.json └── template.yaml ├── node-webpack-custom-build ├── .gitignore ├── README.md ├── data.json ├── events │ └── event.json ├── hello-world │ ├── .npmignore │ ├── Makefile │ ├── app.js │ ├── package.json │ ├── tests │ │ └── unit │ │ │ └── test-handler.js │ └── webpack.config.js └── template.yaml ├── s12d ├── client │ ├── .env │ ├── .gitignore │ ├── README.md │ ├── babel.config.js │ ├── package.json │ ├── postcss.config.js │ ├── public │ │ ├── favicon.ico │ │ └── index.html │ ├── src │ │ ├── App.vue │ │ ├── assets │ │ │ └── tailwind.css │ │ ├── components │ │ │ └── BarChart.js │ │ ├── main.js │ │ ├── router │ │ │ └── index.js │ │ ├── store │ │ │ ├── auth.js │ │ │ ├── index.js │ │ │ └── links.js │ │ └── views │ │ │ ├── Create.vue │ │ │ ├── Dashboard.vue │ │ │ └── Details.vue │ ├── tailwind.config.js │ └── vue.config.js ├── server │ ├── api.yaml │ ├── event.json │ ├── locals.json │ ├── script.yml │ ├── src │ │ ├── analytics │ │ │ └── app.js │ │ └── login │ │ │ └── app.js │ └── template.yaml └── temp.json ├── safe-deploy ├── README.md ├── layer │ └── nodejs │ │ ├── package-lock.json │ │ └── package.json ├── src │ ├── base.js │ └── hooks │ │ ├── basepost.js │ │ └── basepre.js └── template.yaml ├── sam-containers-demo-app ├── .gitignore ├── README.md ├── events │ └── event.json ├── goodbye-world │ ├── .npmignore │ ├── Dockerfile │ ├── app.js │ ├── package.json │ └── tests │ │ └── unit │ │ └── test-handler.js ├── hello-world │ ├── .npmignore │ ├── Dockerfile │ ├── app.js │ ├── package.json │ └── tests │ │ └── unit │ │ └── test-handler.js └── template.yaml ├── sam-or-cdk ├── README.md ├── cdk │ ├── .gitignore │ ├── .npmignore │ ├── README.md │ ├── bin │ │ └── signedurl.ts │ ├── cdk.json │ ├── events │ │ └── fetchUrl.json │ ├── jest.config.js │ ├── lambda │ │ ├── downloadSigner.ts │ │ ├── fetchShortUrl.ts │ │ └── uploadSigner.ts │ ├── lib │ │ └── signedurl-stack.ts │ ├── package.json │ ├── test │ │ └── signedurl.test.ts │ └── tsconfig.json └── sam │ ├── .gitignore │ ├── README.md │ ├── events │ ├── event.json │ └── fetchUrl.json │ ├── lambda │ ├── downloadSigner │ │ ├── .npmignore │ │ ├── app.js │ │ ├── package.json │ │ └── tests │ │ │ └── unit │ │ │ └── test-handler.js │ ├── fetchShortUrl │ │ ├── .npmignore │ │ ├── app.js │ │ ├── package.json │ │ └── tests │ │ │ └── unit │ │ │ └── test-handler.js │ └── uploadSigner │ │ ├── .npmignore │ │ ├── app.js │ │ ├── package.json │ │ └── tests │ │ └── unit │ │ └── test-handler.js │ └── template.yaml ├── secrets ├── README.md ├── app │ ├── src │ │ └── app.js │ └── template.yaml └── create-secrets │ └── template.yaml ├── starter-templates ├── web-app.zip └── web-app │ ├── .gitignore │ ├── .npmignore │ ├── README.md │ ├── __tests__ │ └── unit │ │ ├── get-all-items.test.js │ │ ├── get-by-id.test.js │ │ └── put-item.test.js │ ├── buildspec.yaml │ ├── env.json │ ├── events │ ├── event-get-all-items.json │ ├── event-get-by-id.json │ └── event-post-item.json │ ├── package.json │ ├── src │ ├── get-all-items.js │ ├── get-by-id.js │ └── put-item.js │ └── template.yaml ├── step-functions ├── .gitignore ├── README.md ├── statemachine │ ├── analytics.asl.json │ ├── stackoverflow.asl.json │ └── twitch.asl.json └── template.yaml └── swift-custom-runtime ├── .gitignore ├── Dockerfile ├── Makefile ├── Package.swift ├── README.md ├── Sources ├── Squared │ └── main.swift └── SwiftApi │ └── main.swift ├── events ├── api-event.json └── squared-event.json └── template.yaml /.gitignore: -------------------------------------------------------------------------------- 1 | *.toml 2 | node_modules 3 | .aws-sam 4 | .DS_Store 5 | .vscode 6 | .swiftpm 7 | .stackery 8 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | ## Code of Conduct 2 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 3 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 4 | opensource-codeofconduct@amazon.com with any additional questions or comments. 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software is furnished to do so. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | -------------------------------------------------------------------------------- /SQS/.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/api/osx,node,linux,windows 3 | 4 | ### Linux ### 5 | *~ 6 | 7 | # temporary files which can be created if a process still has a handle open of a deleted file 8 | .fuse_hidden* 9 | 10 | # KDE directory preferences 11 | .directory 12 | 13 | # Linux trash folder which might appear on any partition or disk 14 | .Trash-* 15 | 16 | # .nfs files are created when an open file is removed but is still being accessed 17 | .nfs* 18 | 19 | ### Node ### 20 | # Logs 21 | logs 22 | *.log 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | 27 | # Runtime data 28 | pids 29 | *.pid 30 | *.seed 31 | *.pid.lock 32 | 33 | # Directory for instrumented libs generated by jscoverage/JSCover 34 | lib-cov 35 | 36 | # Coverage directory used by tools like istanbul 37 | coverage 38 | 39 | # nyc test coverage 40 | .nyc_output 41 | 42 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 43 | .grunt 44 | 45 | # Bower dependency directory (https://bower.io/) 46 | bower_components 47 | 48 | # node-waf configuration 49 | .lock-wscript 50 | 51 | # Compiled binary addons (http://nodejs.org/api/addons.html) 52 | build/Release 53 | 54 | # Dependency directories 55 | node_modules/ 56 | jspm_packages/ 57 | 58 | # Typescript v1 declaration files 59 | typings/ 60 | 61 | # Optional npm cache directory 62 | .npm 63 | 64 | # Optional eslint cache 65 | .eslintcache 66 | 67 | # Optional REPL history 68 | .node_repl_history 69 | 70 | # Output of 'npm pack' 71 | *.tgz 72 | 73 | # Yarn Integrity file 74 | .yarn-integrity 75 | 76 | # dotenv environment variables file 77 | .env 78 | 79 | 80 | ### OSX ### 81 | *.DS_Store 82 | .AppleDouble 83 | .LSOverride 84 | 85 | # Icon must end with two \r 86 | Icon 87 | 88 | # Thumbnails 89 | ._* 90 | 91 | # Files that might appear in the root of a volume 92 | .DocumentRevisions-V100 93 | .fseventsd 94 | .Spotlight-V100 95 | .TemporaryItems 96 | .Trashes 97 | .VolumeIcon.icns 98 | .com.apple.timemachine.donotpresent 99 | 100 | # Directories potentially created on remote AFP share 101 | .AppleDB 102 | .AppleDesktop 103 | Network Trash Folder 104 | Temporary Items 105 | .apdisk 106 | 107 | ### Windows ### 108 | # Windows thumbnail cache files 109 | Thumbs.db 110 | ehthumbs.db 111 | ehthumbs_vista.db 112 | 113 | # Folder config file 114 | Desktop.ini 115 | 116 | # Recycle Bin used on file shares 117 | $RECYCLE.BIN/ 118 | 119 | # Windows Installer files 120 | *.cab 121 | *.msi 122 | *.msm 123 | *.msp 124 | 125 | # Windows shortcuts 126 | *.lnk 127 | 128 | 129 | # End of https://www.gitignore.io/api/osx,node,linux,windows -------------------------------------------------------------------------------- /SQS/README.md: -------------------------------------------------------------------------------- 1 | 16 | 17 | # SQS 18 | 19 | Builds an SQS queue with a queue policy sets the queue as an eventsource for a Lambda 20 | 21 | *be sure to update the principals in the SAM template before deploying* -------------------------------------------------------------------------------- /SQS/src/.npmignore: -------------------------------------------------------------------------------- 1 | tests/* 2 | -------------------------------------------------------------------------------- /SQS/src/app.js: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | // // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this 5 | // software and associated documentation files (the "Software"), to deal in the Software 6 | // without restriction, including without limitation the rights to use, copy, modify, 7 | // merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 8 | // permit persons to whom the Software is furnished to do so. 9 | // // 10 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 11 | // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 12 | // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 13 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 14 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 15 | // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 16 | 17 | exports.lambdaHandler = async (event) => { 18 | event.Records.map(record => console.log(JSON.stringify(record))) 19 | return JSON.stringify(event); 20 | } -------------------------------------------------------------------------------- /SQS/src/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hello_world", 3 | "version": "1.0.0", 4 | "description": "hello world sample for NodeJS", 5 | "main": "app.js", 6 | "repository": "https://github.com/awslabs/aws-sam-cli/tree/develop/samcli/local/init/templates/cookiecutter-aws-sam-hello-nodejs", 7 | "author": "SAM CLI", 8 | "license": "MIT", 9 | "dependencies": {}, 10 | "scripts": { 11 | "test": "mocha tests/unit/" 12 | }, 13 | "devDependencies": { 14 | "chai": "^4.2.0", 15 | "mocha": "^6.1.4" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /SQS/src/tests/unit/test-handler.js: -------------------------------------------------------------------------------- 1 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this 2 | // software and associated documentation files (the "Software"), to deal in the Software 3 | // without restriction, including without limitation the rights to use, copy, modify, 4 | // merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 5 | // permit persons to whom the Software is furnished to do so. 6 | // // 7 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 8 | // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 9 | // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 10 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 11 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 12 | // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 13 | 14 | 'use strict'; 15 | 16 | const app = require('../../app.js'); 17 | const chai = require('chai'); 18 | const expect = chai.expect; 19 | var event, context; 20 | 21 | describe('Tests index', function () { 22 | it('verifies successful response', async () => { 23 | const result = await app.lambdaHandler(event, context) 24 | 25 | expect(result).to.be.an('object'); 26 | expect(result.statusCode).to.equal(200); 27 | expect(result.body).to.be.an('string'); 28 | 29 | let response = JSON.parse(result.body); 30 | 31 | expect(response).to.be.an('object'); 32 | expect(response.message).to.be.equal("hello world"); 33 | // expect(response.location).to.be.an("string"); 34 | }); 35 | }); 36 | -------------------------------------------------------------------------------- /SQS/template.yaml: -------------------------------------------------------------------------------- 1 | AWSTemplateFormatVersion: '2010-09-09' 2 | Transform: AWS::Serverless-2016-10-31 3 | Description: SQS 4 | 5 | Globals: 6 | Function: 7 | Timeout: 5 8 | CodeUri: src/ 9 | Runtime: nodejs16.x 10 | 11 | Resources: 12 | DeadLetter: 13 | Type: AWS::SQS::Queue 14 | 15 | RawQueue: 16 | Type: AWS::SQS::Queue 17 | Properties: 18 | DelaySeconds: 0 19 | # FifoQueue: true 20 | # QueueName: myname.fifo 21 | # ContentBasedDeduplication: true 22 | # KmsDataKeyReusePeriodSeconds: 5 23 | # KmsMasterKeyId: /aws/kms/sqs 24 | # MaximumMessageSize: 262,144 25 | # MessageRetentionPeriod: 1209600 26 | # ReceiveMessageWaitTimeSeconds: 10 27 | RedrivePolicy: 28 | deadLetterTargetArn : !GetAtt DeadLetter.Arn 29 | maxReceiveCount : 4 30 | Tags: 31 | - Key: Series 32 | Value: SWS 33 | VisibilityTimeout: 120 34 | 35 | RawQueuePolicy: 36 | Type: AWS::SQS::QueuePolicy 37 | Properties: 38 | PolicyDocument: 39 | Id: Policy1588221050082 40 | Version: '2012-10-17' 41 | Statement: 42 | - Effect: Allow 43 | Action: 44 | - sqs:SendMessage 45 | Resource: !GetAtt RawQueue.Arn 46 | Principal: 47 | ### Update this with your desired principals ### 48 | AWS: 49 | - [desired account number] 50 | - [desired user or role ARN] 51 | Condition: 52 | DateGreaterThan: 53 | aws:CurrentTime: 2020-04-28T12:00Z 54 | DateLessThan: 55 | aws:CurrentTime: 2020-05-01T12:00Z 56 | Queues: 57 | - !Ref RawQueue 58 | 59 | TriggeredFunction: 60 | Type: AWS::Serverless::Function 61 | Properties: 62 | Handler: app.lambdaHandler 63 | Events: 64 | SQSEvent: 65 | Type: SQS 66 | Properties: 67 | Queue: !GetAtt RawQueue.Arn 68 | BatchSize: 10 69 | Enabled: true 70 | 71 | Outputs: 72 | RawSqsUrl: 73 | Description: URL of the SQS Queue 74 | Value: !Ref RawQueue 75 | 76 | DeadLetterUrl: 77 | Description: URL of the SQS Queue 78 | Value: !Ref DeadLetter -------------------------------------------------------------------------------- /api-enhanced-observability-variables/README.md: -------------------------------------------------------------------------------- 1 | # API Gateway enhanced observability variables 2 | 3 | This example demonstrates using the new API Gateway enhanced observability variables to get the full story of an API Gateway request and response. 4 | 5 | ## Requirements 6 | * AWS SAM CLI 1.0.0+ 7 | 8 | ## Deployment 9 | From the root of the project: 10 | ```bash 11 | sam deploy -g 12 | ``` 13 | 14 | ## Teardown: 15 | Using the AWS CLI 16 | ```bash 17 | aws cloudformation delete-stack --stack-name 18 | ``` -------------------------------------------------------------------------------- /api-enhanced-observability-variables/src/app.js: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | // // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this 5 | // software and associated documentation files (the "Software"), to deal in the Software 6 | // without restriction, including without limitation the rights to use, copy, modify, 7 | // merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 8 | // permit persons to whom the Software is furnished to do so. 9 | // // 10 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 11 | // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 12 | // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 13 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 14 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 15 | // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 16 | 17 | exports.lambdaHandler = async (event) => { 18 | return { 19 | "statusCode": 200, 20 | "body": JSON.stringify(event), 21 | "headers": { 22 | "Content-Type": "*/*" 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /appconfig-lambda-extensions/src/app.js: -------------------------------------------------------------------------------- 1 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this 2 | // software and associated documentation files (the "Software"), to deal in the Software 3 | // without restriction, including without limitation the rights to use, copy, modify, 4 | // merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 5 | // permit persons to whom the Software is furnished to do so. 6 | // // 7 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 8 | // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 9 | // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 10 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 11 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 12 | // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 13 | 14 | 'use strict' 15 | const fetch = require('node-fetch') 16 | let message 17 | 18 | async function getConfig () { 19 | try { 20 | const url = 'http://localhost:2772/applications/' + process.env.APPCONFIG_APPLICATION + '/environments/' + process.env.APPCONFIG_ENVIRONMENT + '/configurations/' + process.env.APPCONFIG_CONFIGURATION 21 | const response = await fetch(url) 22 | const json = await response.json() 23 | return json 24 | } catch (err) { 25 | console.error(err) 26 | throw err 27 | } 28 | } 29 | 30 | exports.handler = async (event) => { 31 | const options = await getConfig() 32 | if (options.isEnabled === true) { 33 | message = 'Hello from ' + options.messageOption + '!' 34 | } else { 35 | message = 'Hello from Lambda!' 36 | } 37 | const response = { 38 | statusCode: 200, 39 | body: JSON.stringify(message), 40 | }; 41 | return response; 42 | }; 43 | -------------------------------------------------------------------------------- /appconfig-lambda-extensions/src/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "appconfig-lambda-extensions", 3 | "version": "0.0.1", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "data-uri-to-buffer": { 8 | "version": "4.0.0", 9 | "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.0.tgz", 10 | "integrity": "sha512-Vr3mLBA8qWmcuschSLAOogKgQ/Jwxulv3RNE4FXnYWRGujzrRWQI4m12fQqRkwX06C0KanhLr4hK+GydchZsaA==" 11 | }, 12 | "fetch-blob": { 13 | "version": "3.2.0", 14 | "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", 15 | "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", 16 | "requires": { 17 | "node-domexception": "^1.0.0", 18 | "web-streams-polyfill": "^3.0.3" 19 | } 20 | }, 21 | "formdata-polyfill": { 22 | "version": "4.0.10", 23 | "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", 24 | "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", 25 | "requires": { 26 | "fetch-blob": "^3.1.2" 27 | } 28 | }, 29 | "node-domexception": { 30 | "version": "1.0.0", 31 | "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", 32 | "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==" 33 | }, 34 | "node-fetch": { 35 | "version": "3.2.10", 36 | "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.2.10.tgz", 37 | "integrity": "sha512-MhuzNwdURnZ1Cp4XTazr69K0BTizsBroX7Zx3UgDSVcZYKF/6p0CBe4EUb/hLqmzVhl0UpYfgRljQ4yxE+iCxA==", 38 | "requires": { 39 | "data-uri-to-buffer": "^4.0.0", 40 | "fetch-blob": "^3.1.4", 41 | "formdata-polyfill": "^4.0.10" 42 | } 43 | }, 44 | "web-streams-polyfill": { 45 | "version": "3.2.1", 46 | "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz", 47 | "integrity": "sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==" 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /appconfig-lambda-extensions/src/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "appconfig-lambda-extensions", 3 | "version": "0.0.1", 4 | "description": "", 5 | "dependencies": { 6 | "node-fetch": "^3.2.10" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /appsync-singletable/cdk/.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 | -------------------------------------------------------------------------------- /appsync-singletable/cdk/.npmignore: -------------------------------------------------------------------------------- 1 | *.ts 2 | !*.d.ts 3 | 4 | # CDK asset staging directory 5 | .cdk.staging 6 | cdk.out 7 | -------------------------------------------------------------------------------- /appsync-singletable/cdk/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 | -------------------------------------------------------------------------------- /appsync-singletable/cdk/bin/appsync-cdk.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import 'source-map-support/register'; 3 | import * as cdk from '@aws-cdk/core'; 4 | import { AppsyncCdkStack } from '../lib/appsync-cdk-stack'; 5 | 6 | const app = new cdk.App(); 7 | new AppsyncCdkStack(app, 'AppsyncCdkStack', { 8 | /* If you don't specify 'env', this stack will be environment-agnostic. 9 | * Account/Region-dependent features and context lookups will not work, 10 | * but a single synthesized template can be deployed anywhere. */ 11 | 12 | /* Uncomment the next line to specialize this stack for the AWS Account 13 | * and Region that are implied by the current CLI configuration. */ 14 | // env: { account: process.env.CDK_DEFAULT_ACCOUNT, region: process.env.CDK_DEFAULT_REGION }, 15 | 16 | /* Uncomment the next line if you know exactly what Account and Region you 17 | * want to deploy the stack to. */ 18 | // env: { account: '123456789012', region: 'us-east-1' }, 19 | 20 | /* For more information, see https://docs.aws.amazon.com/cdk/latest/guide/environments.html */ 21 | }); 22 | -------------------------------------------------------------------------------- /appsync-singletable/cdk/cdk.json: -------------------------------------------------------------------------------- 1 | { 2 | "app": "npx ts-node --prefer-ts-exts bin/appsync-cdk.ts", 3 | "context": { 4 | "@aws-cdk/aws-apigateway:usagePlanKeyOrderInsensitiveId": true, 5 | "@aws-cdk/core:enableStackNameDuplicates": "true", 6 | "aws-cdk:enableDiffNoFail": "true", 7 | "@aws-cdk/core:stackRelativeExports": "true", 8 | "@aws-cdk/aws-ecr-assets:dockerIgnoreSupport": true, 9 | "@aws-cdk/aws-secretsmanager:parseOwnedSecretName": true, 10 | "@aws-cdk/aws-kms:defaultKeyPolicies": true, 11 | "@aws-cdk/aws-s3:grantWriteWithoutAcl": true, 12 | "@aws-cdk/aws-ecs-patterns:removeDefaultDesiredCount": true, 13 | "@aws-cdk/aws-rds:lowercaseDbIdentifier": true, 14 | "@aws-cdk/aws-efs:defaultEncryptionAtRest": true, 15 | "@aws-cdk/aws-lambda:recognizeVersionProps": true 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /appsync-singletable/cdk/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | testEnvironment: 'node', 3 | roots: ['/test'], 4 | testMatch: ['**/*.test.ts'], 5 | transform: { 6 | '^.+\\.tsx?$': 'ts-jest' 7 | } 8 | }; 9 | -------------------------------------------------------------------------------- /appsync-singletable/cdk/lib/schema.graphql: -------------------------------------------------------------------------------- 1 | type Parent{ 2 | PK: String! 3 | SK: String! 4 | children: [Child] 5 | data: String! 6 | type: String! 7 | } 8 | 9 | type Child { 10 | PK: String! 11 | SK: String! 12 | data: String! 13 | type: String! 14 | } 15 | 16 | type Mutation { 17 | createParentItem( 18 | PK: ID!, 19 | SK: String!, 20 | data: String!, 21 | type: String! 22 | ): Parent 23 | 24 | createChildItem( 25 | PK: ID!, 26 | SK: String!, 27 | data: String!, 28 | type: String! 29 | ): Child 30 | } 31 | 32 | type Query{ 33 | getParentWithChildren(PK: ID!): Parent 34 | } -------------------------------------------------------------------------------- /appsync-singletable/cdk/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "appsync-cdk", 3 | "version": "0.1.0", 4 | "bin": { 5 | "appsync-cdk": "bin/appsync-cdk.js" 6 | }, 7 | "scripts": { 8 | "build": "tsc", 9 | "watch": "tsc -w", 10 | "test": "jest", 11 | "cdk": "cdk" 12 | }, 13 | "devDependencies": { 14 | "@aws-cdk/assert": "1.195.0", 15 | "@types/jest": "^26.0.10", 16 | "@types/node": "10.17.27", 17 | "aws-cdk": "1.189.0", 18 | "jest": "^26.4.2", 19 | "ts-jest": "^26.2.0", 20 | "ts-node": "^9.0.0", 21 | "typescript": "~3.9.7" 22 | }, 23 | "dependencies": { 24 | "@aws-cdk/aws-appsync": "^1.195.0", 25 | "@aws-cdk/aws-dynamodb": "^1.195.0", 26 | "@aws-cdk/core": "1.195.0", 27 | "source-map-support": "^0.5.16" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /appsync-singletable/cdk/test/appsync-cdk.test.ts: -------------------------------------------------------------------------------- 1 | import { expect as expectCDK, matchTemplate, MatchStyle } from '@aws-cdk/assert'; 2 | import * as cdk from '@aws-cdk/core'; 3 | import * as AppsyncCdk from '../lib/appsync-cdk-stack'; 4 | 5 | test('Empty Stack', () => { 6 | const app = new cdk.App(); 7 | // WHEN 8 | const stack = new AppsyncCdk.AppsyncCdkStack(app, 'MyTestStack'); 9 | // THEN 10 | expectCDK(stack).to(matchTemplate({ 11 | "Resources": {} 12 | }, MatchStyle.EXACT)) 13 | }); 14 | -------------------------------------------------------------------------------- /appsync-singletable/cdk/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2018", 4 | "module": "commonjs", 5 | "lib": [ 6 | "es2018" 7 | ], 8 | "declaration": true, 9 | "strict": true, 10 | "noImplicitAny": true, 11 | "strictNullChecks": true, 12 | "noImplicitThis": true, 13 | "alwaysStrict": true, 14 | "noUnusedLocals": false, 15 | "noUnusedParameters": false, 16 | "noImplicitReturns": true, 17 | "noFallthroughCasesInSwitch": false, 18 | "inlineSourceMap": true, 19 | "inlineSources": true, 20 | "experimentalDecorators": true, 21 | "strictPropertyInitialization": false, 22 | "typeRoots": [ 23 | "./node_modules/@types" 24 | ] 25 | }, 26 | "exclude": [ 27 | "node_modules", 28 | "cdk.out" 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /appsync-singletable/sam/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/sessions-with-aws-sam/78ebaa71baadf092e13e635c1a0e89e7c49f802f/appsync-singletable/sam/__init__.py -------------------------------------------------------------------------------- /appsync-singletable/sam/events/event.json: -------------------------------------------------------------------------------- 1 | { 2 | "body": "{\"message\": \"hello world\"}", 3 | "resource": "/{proxy+}", 4 | "path": "/path/to/resource", 5 | "httpMethod": "POST", 6 | "isBase64Encoded": false, 7 | "queryStringParameters": { 8 | "foo": "bar" 9 | }, 10 | "pathParameters": { 11 | "proxy": "/path/to/resource" 12 | }, 13 | "stageVariables": { 14 | "baz": "qux" 15 | }, 16 | "headers": { 17 | "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", 18 | "Accept-Encoding": "gzip, deflate, sdch", 19 | "Accept-Language": "en-US,en;q=0.8", 20 | "Cache-Control": "max-age=0", 21 | "CloudFront-Forwarded-Proto": "https", 22 | "CloudFront-Is-Desktop-Viewer": "true", 23 | "CloudFront-Is-Mobile-Viewer": "false", 24 | "CloudFront-Is-SmartTV-Viewer": "false", 25 | "CloudFront-Is-Tablet-Viewer": "false", 26 | "CloudFront-Viewer-Country": "US", 27 | "Host": "1234567890.execute-api.us-east-1.amazonaws.com", 28 | "Upgrade-Insecure-Requests": "1", 29 | "User-Agent": "Custom User Agent String", 30 | "Via": "1.1 08f323deadbeefa7af34d5feb414ce27.cloudfront.net (CloudFront)", 31 | "X-Amz-Cf-Id": "cDehVQoZnx43VYQb9j2-nvCh-9z396Uhbp027Y2JvkCPNLmGJHqlaA==", 32 | "X-Forwarded-For": "127.0.0.1, 127.0.0.2", 33 | "X-Forwarded-Port": "443", 34 | "X-Forwarded-Proto": "https" 35 | }, 36 | "requestContext": { 37 | "accountId": "123456789012", 38 | "resourceId": "123456", 39 | "stage": "prod", 40 | "requestId": "c6af9ac6-7b61-11e6-9a41-93e8deadbeef", 41 | "requestTime": "09/Apr/2015:12:34:56 +0000", 42 | "requestTimeEpoch": 1428582896000, 43 | "identity": { 44 | "cognitoIdentityPoolId": null, 45 | "accountId": null, 46 | "cognitoIdentityId": null, 47 | "caller": null, 48 | "accessKey": null, 49 | "sourceIp": "127.0.0.1", 50 | "cognitoAuthenticationType": null, 51 | "cognitoAuthenticationProvider": null, 52 | "userArn": null, 53 | "userAgent": "Custom User Agent String", 54 | "user": null 55 | }, 56 | "path": "/prod/path/to/resource", 57 | "resourcePath": "/{proxy+}", 58 | "httpMethod": "POST", 59 | "apiId": "1234567890", 60 | "protocol": "HTTP/1.1" 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /appsync-singletable/sam/hello_world/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/sessions-with-aws-sam/78ebaa71baadf092e13e635c1a0e89e7c49f802f/appsync-singletable/sam/hello_world/__init__.py -------------------------------------------------------------------------------- /appsync-singletable/sam/hello_world/app.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | # import requests 4 | 5 | 6 | def lambda_handler(event, context): 7 | """Sample pure Lambda function 8 | 9 | Parameters 10 | ---------- 11 | event: dict, required 12 | API Gateway Lambda Proxy Input Format 13 | 14 | Event doc: https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html#api-gateway-simple-proxy-for-lambda-input-format 15 | 16 | context: object, required 17 | Lambda Context runtime methods and attributes 18 | 19 | Context doc: https://docs.aws.amazon.com/lambda/latest/dg/python-context-object.html 20 | 21 | Returns 22 | ------ 23 | API Gateway Lambda Proxy Output Format: dict 24 | 25 | Return doc: https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html 26 | """ 27 | 28 | # try: 29 | # ip = requests.get("http://checkip.amazonaws.com/") 30 | # except requests.RequestException as e: 31 | # # Send some context about this error to Lambda Logs 32 | # print(e) 33 | 34 | # raise e 35 | 36 | return { 37 | "statusCode": 200, 38 | "body": json.dumps({ 39 | "message": "hello world", 40 | # "location": ip.text.replace("\n", "") 41 | }), 42 | } 43 | -------------------------------------------------------------------------------- /appsync-singletable/sam/hello_world/requirements.txt: -------------------------------------------------------------------------------- 1 | requests -------------------------------------------------------------------------------- /appsync-singletable/sam/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/sessions-with-aws-sam/78ebaa71baadf092e13e635c1a0e89e7c49f802f/appsync-singletable/sam/tests/__init__.py -------------------------------------------------------------------------------- /appsync-singletable/sam/tests/integration/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/sessions-with-aws-sam/78ebaa71baadf092e13e635c1a0e89e7c49f802f/appsync-singletable/sam/tests/integration/__init__.py -------------------------------------------------------------------------------- /appsync-singletable/sam/tests/integration/test_api_gateway.py: -------------------------------------------------------------------------------- 1 | import os 2 | from unittest import TestCase 3 | 4 | import boto3 5 | import requests 6 | 7 | """ 8 | Make sure env variable AWS_SAM_STACK_NAME exists with the name of the stack we are going to test. 9 | """ 10 | 11 | 12 | class TestApiGateway(TestCase): 13 | api_endpoint: str 14 | 15 | @classmethod 16 | def get_stack_name(cls) -> str: 17 | stack_name = os.environ.get("AWS_SAM_STACK_NAME") 18 | if not stack_name: 19 | raise Exception( 20 | "Cannot find env var AWS_SAM_STACK_NAME. \n" 21 | "Please setup this environment variable with the stack name where we are running integration tests." 22 | ) 23 | 24 | return stack_name 25 | 26 | def setUp(self) -> None: 27 | """ 28 | Based on the provided env variable AWS_SAM_STACK_NAME, 29 | here we use cloudformation API to find out what the HelloWorldApi URL is 30 | """ 31 | stack_name = TestApiGateway.get_stack_name() 32 | 33 | client = boto3.client("cloudformation") 34 | 35 | try: 36 | response = client.describe_stacks(StackName=stack_name) 37 | except Exception as e: 38 | raise Exception( 39 | f"Cannot find stack {stack_name}. \n" f'Please make sure stack with the name "{stack_name}" exists.' 40 | ) from e 41 | 42 | stacks = response["Stacks"] 43 | 44 | stack_outputs = stacks[0]["Outputs"] 45 | api_outputs = [output for output in stack_outputs if output["OutputKey"] == "HelloWorldApi"] 46 | self.assertTrue(api_outputs, f"Cannot find output HelloWorldApi in stack {stack_name}") 47 | 48 | self.api_endpoint = api_outputs[0]["OutputValue"] 49 | 50 | def test_api_gateway(self): 51 | """ 52 | Call the API Gateway endpoint and check the response 53 | """ 54 | response = requests.get(self.api_endpoint) 55 | self.assertDictEqual(response.json(), {"message": "hello world"}) 56 | -------------------------------------------------------------------------------- /appsync-singletable/sam/tests/requirements.txt: -------------------------------------------------------------------------------- 1 | pytest 2 | pytest-mock 3 | boto3 -------------------------------------------------------------------------------- /appsync-singletable/sam/tests/unit/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/sessions-with-aws-sam/78ebaa71baadf092e13e635c1a0e89e7c49f802f/appsync-singletable/sam/tests/unit/__init__.py -------------------------------------------------------------------------------- /cognito/README.md: -------------------------------------------------------------------------------- 1 | 16 | 17 | # Amazon Cognito 18 | 19 | Template to create a cognito backend to use with HTTP APIs JWT authorizer. Includes option to add Cognito user groups as custom scopes for route access. -------------------------------------------------------------------------------- /cognito/src/app.js: -------------------------------------------------------------------------------- 1 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this 2 | // software and associated documentation files (the "Software"), to deal in the Software 3 | // without restriction, including without limitation the rights to use, copy, modify, 4 | // merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 5 | // permit persons to whom the Software is furnished to do so. 6 | // // 7 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 8 | // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 9 | // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 10 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 11 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 12 | // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 13 | 14 | exports.lambdaHandler = async (event, context, callback) => { 15 | let newScopes = event.request.groupConfiguration.groupsToOverride.map(item => `${item}-${event.callerContext.clientId}`) 16 | 17 | event.response = { 18 | "claimsOverrideDetails": { 19 | "claimsToAddOrOverride": { 20 | "scope": newScopes.join(" "), 21 | } 22 | } 23 | }; 24 | 25 | callback(null, event) 26 | } -------------------------------------------------------------------------------- /custom-domains/README.md: -------------------------------------------------------------------------------- 1 | 16 | 17 | # Custom domains 18 | These templates show different ways to create custom domains for HTTP API and REST API. If a ZoneID is passed in then the recordset is created in the existing zone. If not, then a new Zone is created with the record set in it. If the CERT ARN is passed in then that certificate will be used. If not, then a new cert is created. 19 | 20 | ## Deployment 21 | use `sam deploy -g` in each folder to get started. 22 | 23 | ## Cert verification 24 | If a cert arn is NOT passed in, then during the creation of the **GeneratedCert** you will be asked to create a CNAME record in the route 53 zone. Watch for *Content of DNS Record is* It will then continue on with the deploy after the DNS has been verified. 25 | 26 | ## Types 27 | ### HTTP 28 | Builds a custom domain and points it to the root of an HTTP API 29 | 30 | ### REST 31 | Builds a custom domain and points it to the root of an REST API 32 | 33 | ### both-declared 34 | Builds a custom domain and the custom domain mappings for an HTTP API and a REST API declaratively. 35 | 36 | ### both-implied 37 | Creates a custom domain for an HTTP API via the domain property on the HTTP API. It then creates an API mapping for a REST API for the same custom domain. -------------------------------------------------------------------------------- /custom-domains/http/template.yaml: -------------------------------------------------------------------------------- 1 | AWSTemplateFormatVersion: '2010-09-09' 2 | Transform: AWS::Serverless-2016-10-31 3 | Description: Custom Domain 4 | 5 | Parameters: 6 | DomainName: 7 | Type: String 8 | Description: Domain name for api 9 | ZoneId: 10 | Type: String 11 | Description: Zone ID if exists. If not leave as none. 12 | Default: none 13 | CertArn: 14 | Type: String 15 | Description: Certificate ARN if exists. If not leave as none. 16 | Default: none 17 | 18 | Conditions: 19 | CreateZone: 20 | !Equals [!Ref ZoneId, 'none'] 21 | CreateCert: 22 | !Equals [!Ref CertArn, 'none'] 23 | 24 | Resources: 25 | GeneratedZone: # If a Zone ID is not passed in the parameteres, then a new zone is created for the domain 26 | Type: AWS::Route53::HostedZone 27 | Condition: CreateZone 28 | Properties: 29 | Name: !Ref DomainName 30 | 31 | GeneratedCert: # If a Certificate ARN is not passed in the parameters, then a new cert is created and will required validation during the deploy 32 | Type: AWS::CertificateManager::Certificate 33 | Condition: CreateCert 34 | Properties: 35 | DomainName: !Ref DomainName 36 | ValidationMethod: DNS 37 | 38 | HttpApiGateway: # Creates a HTTP API endpoint under the customm domain 39 | Type: AWS::Serverless::HttpApi 40 | Properties: 41 | Domain: 42 | DomainName: !Ref DomainName 43 | CertificateArn: !If [CreateCert, !Ref GeneratedCert, !Ref CertArn] 44 | Route53: 45 | HostedZoneId: !If [CreateZone, !Ref GeneratedZone, !Ref ZoneId] 46 | 47 | HttpApiFunction: # Integration Lambda function for the HTTP API 48 | Type: AWS::Serverless::Function 49 | Properties: 50 | InlineCode: exports.handler = async (event) => JSON.stringify(event) 51 | Handler: index.handler 52 | Runtime: nodejs16.x 53 | Timeout: 30 54 | Events: 55 | FetchHttp: 56 | Type: HttpApi 57 | Properties: 58 | ApiId: !Ref HttpApiGateway 59 | Method: GET 60 | Path: / 61 | -------------------------------------------------------------------------------- /custom-domains/rest/template.yaml: -------------------------------------------------------------------------------- 1 | AWSTemplateFormatVersion: '2010-09-09' 2 | Transform: AWS::Serverless-2016-10-31 3 | Description: Custom Domain 4 | 5 | Parameters: 6 | DomainName: 7 | Type: String 8 | Description: Domian name for api 9 | ZoneId: 10 | Type: String 11 | Description: Zone ID if exists. If not leave as none. 12 | Default: none 13 | CertArn: 14 | Type: String 15 | Description: Certificate ARN if exists. If not leave as none. 16 | Default: none 17 | 18 | Conditions: 19 | CreateZone: 20 | !Equals [!Ref ZoneId, 'none'] 21 | CreateCert: 22 | !Equals [!Ref CertArn, 'none'] 23 | 24 | Resources: 25 | GeneratedZone: # If a Zone ID is not passed in the parameteres, then a new zone is created for the domain 26 | Type: AWS::Route53::HostedZone 27 | Condition: CreateZone 28 | Properties: 29 | Name: !Ref DomainName 30 | 31 | GeneratedCert: # If a Certificate ARN is not passed in the parameters, then a new cert is created and will required validation during the deploy 32 | Type: AWS::CertificateManager::Certificate 33 | Condition: CreateCert 34 | Properties: 35 | DomainName: !Ref DomainName 36 | ValidationMethod: DNS 37 | 38 | RestApiGateway: # Creates a REST API endpoint under the custom domain 39 | Type: AWS::Serverless::Api 40 | Properties: 41 | StageName: Prod 42 | Domain: 43 | DomainName: !Ref DomainName 44 | CertificateArn: !If [CreateCert, !Ref GeneratedCert, !Ref CertArn] 45 | Route53: 46 | HostedZoneId: !If [CreateZone, !Ref GeneratedZone, !Ref ZoneId] 47 | 48 | RestApiFunction: # Integration Lambda function for the Rest API 49 | Type: AWS::Serverless::Function 50 | Properties: 51 | InlineCode: | 52 | exports.handler = async (event) => { 53 | const response = { 54 | statusCode: 200, 55 | body: JSON.stringify('Hello from Lambda!'), 56 | }; 57 | return response; 58 | }; 59 | Handler: index.handler 60 | Runtime: nodejs16.x 61 | Events: 62 | FetchRest: 63 | Type: Api 64 | Properties: 65 | RestApiId: !Ref RestApiGateway 66 | Method: GET 67 | Path: / -------------------------------------------------------------------------------- /dotnet-api/.gitignore: -------------------------------------------------------------------------------- 1 | .stackery 2 | .aws-sam 3 | -------------------------------------------------------------------------------- /dotnet-api/.stackery-config.yaml: -------------------------------------------------------------------------------- 1 | stack-name: sessions-with-sam-dotnet-api 2 | template-path: template.yaml 3 | -------------------------------------------------------------------------------- /dotnet-api/src/SaveUser/.gitignore: -------------------------------------------------------------------------------- 1 | bin 2 | obj 3 | -------------------------------------------------------------------------------- /dotnet-api/src/SaveUser/.stackery-config.yaml: -------------------------------------------------------------------------------- 1 | function-id: SaveUser 2 | template-path: ../../template.yaml -------------------------------------------------------------------------------- /dotnet-api/src/SaveUser/Function.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.Text.Json; 5 | using System.Threading.Tasks; 6 | using Amazon.DynamoDBv2; 7 | using Amazon.DynamoDBv2.DocumentModel; 8 | using Amazon.Lambda.APIGatewayEvents; 9 | using Amazon.Lambda.Core; 10 | 11 | // Assembly attribute to enable the Lambda function's JSON input to be converted into a .NET class. 12 | [assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.LambdaJsonSerializer))] 13 | 14 | namespace Function 15 | { 16 | public class Function 17 | { 18 | public async Task FunctionHandler(APIGatewayHttpApiV2ProxyRequest eventTrigger) 19 | { 20 | Console.WriteLine(JsonSerializer.Serialize(eventTrigger)); 21 | 22 | var body = eventTrigger.Body; 23 | if (eventTrigger.IsBase64Encoded) { 24 | body = Encoding.UTF8.GetString(Convert.FromBase64String(body)); 25 | } 26 | 27 | // Add "id" from path parameters to JSON body 28 | var data = JsonSerializer.Deserialize>(body); 29 | data["id"] = eventTrigger.PathParameters["id"]; 30 | var dataJSON = JsonSerializer.Serialize(data); 31 | 32 | var client = new AmazonDynamoDBClient(); 33 | var table = Table.LoadTable(client, Environment.GetEnvironmentVariable("TABLE_NAME")); 34 | 35 | await table.PutItemAsync(Document.FromJson(dataJSON)); 36 | 37 | return new APIGatewayHttpApiV2ProxyResponse 38 | { 39 | StatusCode = 204 40 | }; 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /dotnet-api/src/SaveUser/Function.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | netcoreapp3.1 4 | true 5 | Lambda 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /dotnet-api/src/SaveUser/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Using SAM and Stackery to build .Net Lambda functions 3 | 4 | This Function was generated by [Stackery.io](https://www.stackery.io). 5 | 6 | We recommend updating this readme with your own function specific documentation. 7 | 8 | # Dependencies 9 | You can add dependencies on other files and packages. 10 | Within this directory, local dependencies can be added as individual files and 11 | package dependencies can be added to [Function.csproj](https://docs.microsoft.com/en-us/dotnet/articles/core/tools/csproj#packagereference). 12 | Package dependencies will be installed when the stack is deployed. 13 | 14 | # Stackery Documentation 15 | Documentation for Function resources can be found at [https://docs.stackery.io/docs/api/nodes/Function](https://docs.stackery.io/docs/api/nodes/Function/). 16 | -------------------------------------------------------------------------------- /dotnet-api/template.yaml: -------------------------------------------------------------------------------- 1 | AWSTemplateFormatVersion: 2010-09-09 2 | Transform: AWS::Serverless-2016-10-31 3 | Resources: 4 | HttpApi: 5 | Type: AWS::Serverless::HttpApi 6 | Properties: 7 | DefinitionBody: 8 | openapi: '3.0' 9 | info: 10 | title: !Sub ${AWS::StackName}-HttpApi 11 | version: '1.0' 12 | paths: 13 | /users/{id}: 14 | post: 15 | responses: {} 16 | FailOnWarnings: true 17 | Users: 18 | Type: AWS::DynamoDB::Table 19 | Properties: 20 | AttributeDefinitions: 21 | - AttributeName: id 22 | AttributeType: S 23 | BillingMode: PAY_PER_REQUEST 24 | KeySchema: 25 | - AttributeName: id 26 | KeyType: HASH 27 | StreamSpecification: 28 | StreamViewType: NEW_AND_OLD_IMAGES 29 | SaveUser: 30 | Type: AWS::Serverless::Function 31 | Properties: 32 | FunctionName: !Sub ${AWS::StackName}-SaveUser 33 | Description: !Sub 34 | - Stack ${StackTagName} Environment ${EnvironmentTagName} Function ${ResourceName} 35 | - ResourceName: SaveUser 36 | CodeUri: src/SaveUser 37 | Handler: Function::Function.Function::FunctionHandler 38 | Runtime: dotnetcore3.1 39 | MemorySize: 3008 40 | Timeout: 30 41 | Tracing: Active 42 | Policies: 43 | - AWSXrayWriteOnlyAccess 44 | - DynamoDBCrudPolicy: 45 | TableName: !Ref Users 46 | Events: 47 | HttpApiPOSTusersid: 48 | Type: HttpApi 49 | Properties: 50 | Path: /users/{id} 51 | Method: POST 52 | ApiId: !Ref HttpApi 53 | PayloadFormatVersion: '2.0' 54 | TimeoutInMillis: 29000 55 | Environment: 56 | Variables: 57 | TABLE_NAME: !Ref Users 58 | TABLE_ARN: !GetAtt Users.Arn 59 | Parameters: 60 | StackTagName: 61 | Type: String 62 | Description: Stack Name (injected by Stackery at deployment time) 63 | EnvironmentTagName: 64 | Type: String 65 | Description: Environment Name (injected by Stackery at deployment time) -------------------------------------------------------------------------------- /eventbridge-dlq-retry/README.md: -------------------------------------------------------------------------------- 1 | # EventBridge with DLQ and RetryPolicy set 2 | 3 | This template creates an HTTP API with a direct integration to the EventBridge default bus. It also creates an EventBridge rule with a Lambda function as a trigger. The template also configures a DLQ and a retry policy for the EventBridge rule. 4 | 5 | deploy using `sam deploy -g` -------------------------------------------------------------------------------- /eventbridge-dlq-retry/api.yaml: -------------------------------------------------------------------------------- 1 | openapi: "3.0.1" 2 | info: 3 | title: "EventBridge DLQ" 4 | paths: 5 | /eb: 6 | post: 7 | responses: 8 | default: 9 | description: "EventBridge response" 10 | x-amazon-apigateway-integration: 11 | integrationSubtype: "EventBridge-PutEvents" 12 | credentials: 13 | Fn::GetAtt: [MyHttpApiRole, Arn] 14 | requestParameters: 15 | Detail: "$request.body.Detail" 16 | DetailType: "MyDetailType" 17 | Source: "WebApp" 18 | payloadFormatVersion: "1.0" 19 | type: "aws_proxy" 20 | connectionType: "INTERNET" -------------------------------------------------------------------------------- /eventbridge-dlq-retry/src/app.js: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | // // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this 5 | // software and associated documentation files (the "Software"), to deal in the Software 6 | // without restriction, including without limitation the rights to use, copy, modify, 7 | // merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 8 | // permit persons to whom the Software is furnished to do so. 9 | // // 10 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 11 | // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 12 | // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 13 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 14 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 15 | // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 16 | 17 | exports.lambdaHandler = async (event) => { 18 | event.Records.map(record => console.log(JSON.stringify(record))) 19 | return JSON.stringify(event); 20 | } -------------------------------------------------------------------------------- /eventbridge/README.md: -------------------------------------------------------------------------------- 1 | 16 | 17 | # EventBridge 18 | 19 | Creates a custom bus on EventBridge and demonstrates pushing custom events and triggering on rules. 20 | -------------------------------------------------------------------------------- /eventbridge/events/endpoint.json: -------------------------------------------------------------------------------- 1 | { 2 | "body":"{\"type\": \"translate\",\"data\": \"This string should be translated\",\"language\": \"es\"}" 3 | } -------------------------------------------------------------------------------- /eventbridge/events/sentiment.json: -------------------------------------------------------------------------------- 1 | { 2 | "detail": { 3 | "type":"translate", 4 | "data":"Rob is really mean", 5 | "language":"en" 6 | }, 7 | "detailType": "translate", 8 | "source": "TextEndpoint", 9 | "eventBusName": "SuperBus2" 10 | } -------------------------------------------------------------------------------- /eventbridge/events/translate.json: -------------------------------------------------------------------------------- 1 | { 2 | "detail": { 3 | "type":"translate", 4 | "data":"Hey Rob, how ya doing?", 5 | "language":"it" 6 | }, 7 | "detailType": "translate", 8 | "source": "TextEndpoint", 9 | "eventBusName": "SuperBus2" 10 | } -------------------------------------------------------------------------------- /eventbridge/locals.json: -------------------------------------------------------------------------------- 1 | { 2 | "EndpointFunction": { 3 | "EVENT_BUS_NAME": "SuperBus2" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /eventbridge/src/endpoint.js: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | // // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this 5 | // software and associated documentation files (the "Software"), to deal in the Software 6 | // without restriction, including without limitation the rights to use, copy, modify, 7 | // merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 8 | // permit persons to whom the Software is furnished to do so. 9 | // // 10 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 11 | // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 12 | // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 13 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 14 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 15 | // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 16 | 17 | const model = { 18 | "type": "translate", 19 | "data": "This string should be translated", 20 | "language": "es" 21 | } 22 | 23 | const AWS = require('aws-sdk'); 24 | const eventbridge = new AWS.EventBridge(); 25 | 26 | 27 | exports.lambdaHandler = async (event) => { 28 | try { 29 | let requestData = JSON.parse(event.body) 30 | let params = { 31 | Entries: [{ 32 | Detail: JSON.stringify(requestData), 33 | DetailType: requestData.type, 34 | Source: "TextEndpoint", 35 | EventBusName: process.env.EVENT_BUS_NAME 36 | }] 37 | } 38 | 39 | if (requestData) await eventbridge.putEvents(params).promise() 40 | 41 | console.log('Pushed data to EventBridge'); 42 | console.log(params); 43 | 44 | return { 45 | "message": "data received", 46 | "data": requestData 47 | } 48 | } catch (err) { 49 | console.log(err.message); 50 | return { 51 | "message": "Error submitting data", 52 | "error": err.message 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /eventbridge/src/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "src", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "endpoint.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC" 11 | } 12 | -------------------------------------------------------------------------------- /eventbridge/src/sentiment.js: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | // // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this 5 | // software and associated documentation files (the "Software"), to deal in the Software 6 | // without restriction, including without limitation the rights to use, copy, modify, 7 | // merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 8 | // permit persons to whom the Software is furnished to do so. 9 | // // 10 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 11 | // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 12 | // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 13 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 14 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 15 | // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 16 | 17 | const AWS = require('aws-sdk'); 18 | const comprehend = new AWS.Comprehend(); 19 | 20 | exports.lambdaHandler = async (event) => { 21 | try { 22 | let params = { 23 | LanguageCode: event.detail.language, 24 | Text: event.detail.data 25 | } 26 | let sentiment = await comprehend.detectSentiment(params).promise() 27 | console.log(sentiment.Sentiment) 28 | } catch (err) { 29 | console.log(err.message) 30 | } 31 | return { 32 | "success": true 33 | } 34 | } -------------------------------------------------------------------------------- /eventbridge/src/translate.js: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | // // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this 5 | // software and associated documentation files (the "Software"), to deal in the Software 6 | // without restriction, including without limitation the rights to use, copy, modify, 7 | // merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 8 | // permit persons to whom the Software is furnished to do so. 9 | // // 10 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 11 | // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 12 | // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 13 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 14 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 15 | // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 16 | 17 | const AWS = require('aws-sdk'); 18 | const translate = new AWS.Translate(); 19 | 20 | exports.lambdaHandler = async (event) => { 21 | try { 22 | let params = { 23 | SourceLanguageCode: "auto", 24 | TargetLanguageCode: event.detail.language, 25 | Text: event.detail.data 26 | } 27 | let translation = await translate.translateText(params).promise() 28 | console.log(translation.TranslatedText) 29 | } catch (err) { 30 | console.log(err.message) 31 | } 32 | return { 33 | "success": true 34 | } 35 | } -------------------------------------------------------------------------------- /eventbridge/template.yaml: -------------------------------------------------------------------------------- 1 | AWSTemplateFormatVersion: '2010-09-09' 2 | Transform: AWS::Serverless-2016-10-31 3 | Description: EventBridge template 4 | 5 | Globals: 6 | Function: 7 | Timeout: 5 8 | CodeUri: src/ 9 | Runtime: nodejs16.x 10 | 11 | Resources: 12 | CustomBus: 13 | Type: AWS::Events::EventBus 14 | Properties: 15 | Name: SuperBus2 16 | 17 | EndpointFunction: 18 | Type: AWS::Serverless::Function 19 | Properties: 20 | Handler: endpoint.lambdaHandler 21 | Policies: 22 | - EventBridgePutEventsPolicy: 23 | EventBusName: !Ref CustomBus 24 | Environment: 25 | Variables: 26 | EVENT_BUS_NAME: !Ref CustomBus 27 | Events: 28 | API: 29 | Type: HttpApi 30 | Properties: 31 | Path: / 32 | Method: POST 33 | 34 | TranslateFunction: 35 | Type: AWS::Serverless::Function 36 | Properties: 37 | Handler: translate.lambdaHandler 38 | Policies: 39 | - ComprehendBasicAccessPolicy: {} 40 | - Version: '2012-10-17' 41 | Statement: 42 | - Effect: Allow 43 | Action: 44 | - 'translate:TranslateText' 45 | Resource: '*' 46 | Events: 47 | TranslateFilter: 48 | Type: EventBridgeRule 49 | Properties: 50 | # Input: String '{"Key": "Value"}' Data that is sent to target, if used, event is not sent 51 | # InputPath: String path to part of event desired 52 | EventBusName: !Ref CustomBus 53 | Pattern: 54 | source: 55 | - "TextEndpoint" 56 | detail-type: 57 | - "translate" 58 | 59 | SentimentFunction: 60 | Type: AWS::Serverless::Function 61 | Properties: 62 | Handler: sentiment.lambdaHandler 63 | Policies: 64 | - ComprehendBasicAccessPolicy: {} 65 | Events: 66 | SentimentFilter: 67 | Type: EventBridgeRule 68 | Properties: 69 | EventBusName: !Ref CustomBus 70 | Pattern: 71 | source: 72 | - "TextEndpoint" 73 | detail-type: 74 | - "sentiment" 75 | 76 | Outputs: 77 | APIUrl: 78 | Description: "HTTP API endpoint URL" 79 | Value: !Sub "https://${ServerlessHttpApi}.execute-api.${AWS::Region}.amazonaws.com" 80 | MyNotificationArn: 81 | Description: "Topic ARN" 82 | Value: !Ref AWS::NotificationARNs 83 | 84 | -------------------------------------------------------------------------------- /go-al2/hello-world/Makefile: -------------------------------------------------------------------------------- 1 | build-HelloWorldFunction: 2 | GOOS=linux go build -o bootstrap 3 | cp ./bootstrap $(ARTIFACTS_DIR)/. 4 | -------------------------------------------------------------------------------- /go-al2/hello-world/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "io/ioutil" 7 | "net/http" 8 | 9 | "github.com/aws/aws-lambda-go/events" 10 | "github.com/aws/aws-lambda-go/lambda" 11 | ) 12 | 13 | var ( 14 | // DefaultHTTPGetAddress Default Address 15 | DefaultHTTPGetAddress = "https://checkip.amazonaws.com" 16 | 17 | // ErrNoIP No IP found in response 18 | ErrNoIP = errors.New("No IP in HTTP response") 19 | 20 | // ErrNon200Response non 200 status code in response 21 | ErrNon200Response = errors.New("Non 200 Response found") 22 | ) 23 | 24 | func handler(request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { 25 | resp, err := http.Get(DefaultHTTPGetAddress) 26 | if err != nil { 27 | return events.APIGatewayProxyResponse{}, err 28 | } 29 | 30 | if resp.StatusCode != 200 { 31 | return events.APIGatewayProxyResponse{}, ErrNon200Response 32 | } 33 | 34 | ip, err := ioutil.ReadAll(resp.Body) 35 | if err != nil { 36 | return events.APIGatewayProxyResponse{}, err 37 | } 38 | 39 | if len(ip) == 0 { 40 | return events.APIGatewayProxyResponse{}, ErrNoIP 41 | } 42 | 43 | return events.APIGatewayProxyResponse{ 44 | Body: fmt.Sprintf("Hello, %v", string(ip)), 45 | StatusCode: 200, 46 | }, nil 47 | } 48 | 49 | func main() { 50 | lambda.Start(handler) 51 | } 52 | -------------------------------------------------------------------------------- /go-al2/hello-world/main_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "net/http/httptest" 7 | "testing" 8 | 9 | "github.com/aws/aws-lambda-go/events" 10 | ) 11 | 12 | func TestHandler(t *testing.T) { 13 | t.Run("Unable to get IP", func(t *testing.T) { 14 | DefaultHTTPGetAddress = "http://127.0.0.1:12345" 15 | 16 | _, err := handler(events.APIGatewayProxyRequest{}) 17 | if err == nil { 18 | t.Fatal("Error failed to trigger with an invalid request") 19 | } 20 | }) 21 | 22 | t.Run("Non 200 Response", func(t *testing.T) { 23 | ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 24 | w.WriteHeader(500) 25 | })) 26 | defer ts.Close() 27 | 28 | DefaultHTTPGetAddress = ts.URL 29 | 30 | _, err := handler(events.APIGatewayProxyRequest{}) 31 | if err != nil && err.Error() != ErrNon200Response.Error() { 32 | t.Fatalf("Error failed to trigger with an invalid HTTP response: %v", err) 33 | } 34 | }) 35 | 36 | t.Run("Unable decode IP", func(t *testing.T) { 37 | ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 38 | w.WriteHeader(500) 39 | })) 40 | defer ts.Close() 41 | 42 | DefaultHTTPGetAddress = ts.URL 43 | 44 | _, err := handler(events.APIGatewayProxyRequest{}) 45 | if err == nil { 46 | t.Fatal("Error failed to trigger with an invalid HTTP response") 47 | } 48 | }) 49 | 50 | t.Run("Successful Request", func(t *testing.T) { 51 | ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 52 | w.WriteHeader(200) 53 | fmt.Fprintf(w, "127.0.0.1") 54 | })) 55 | defer ts.Close() 56 | 57 | DefaultHTTPGetAddress = ts.URL 58 | 59 | _, err := handler(events.APIGatewayProxyRequest{}) 60 | if err != nil { 61 | t.Fatal("Everything should be ok") 62 | } 63 | }) 64 | } 65 | -------------------------------------------------------------------------------- /go-al2/template.yaml: -------------------------------------------------------------------------------- 1 | AWSTemplateFormatVersion: '2010-09-09' 2 | Transform: AWS::Serverless-2016-10-31 3 | Description: > 4 | go-al2 5 | 6 | Sample SAM Template for go-al2 7 | 8 | # More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst 9 | Globals: 10 | Function: 11 | Timeout: 5 12 | 13 | Resources: 14 | HelloWorldFunction: 15 | Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction 16 | Properties: 17 | CodeUri: hello-world/ 18 | Handler: bootstrap 19 | Runtime: provided.al2 20 | Tracing: Active # https://docs.aws.amazon.com/lambda/latest/dg/lambda-x-ray.html 21 | Events: 22 | CatchAll: 23 | Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api 24 | Properties: 25 | Path: /hello 26 | Method: GET 27 | Environment: # More info about Env Vars: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#environment-object 28 | Variables: 29 | PARAM1: VALUE 30 | Metadata: 31 | BuildMethod: makefile 32 | 33 | Outputs: 34 | # ServerlessRestApi is an implicit API created out of Events key under Serverless::Function 35 | # Find out more about other implicit resources you can reference within SAM 36 | # https://github.com/awslabs/serverless-application-model/blob/master/docs/internals/generated_resources.rst#api 37 | HelloWorldAPI: 38 | Description: "API Gateway endpoint URL for Prod environment for First Function" 39 | Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/hello/" 40 | HelloWorldFunction: 41 | Description: "First Lambda Function ARN" 42 | Value: !GetAtt HelloWorldFunction.Arn 43 | HelloWorldFunctionIamRole: 44 | Description: "Implicit IAM Role created for Hello World function" 45 | Value: !GetAtt HelloWorldFunctionRole.Arn 46 | -------------------------------------------------------------------------------- /governance/1-initial-setup/template.yaml: -------------------------------------------------------------------------------- 1 | AWSTemplateFormatVersion: 2010-09-09 2 | Transform: AWS::Serverless-2016-10-31 3 | Description: Gevernance for Serverless initial setup 4 | 5 | Parameters: 6 | MainRegion: 7 | Type: String 8 | Default: us-west-2 9 | Description: Main region for the account 10 | 11 | Conditions: 12 | IsMainRegion: !Equals [!Ref MainRegion, !Ref "AWS::Region"] 13 | 14 | Resources: 15 | DataBucket: # Region specific bucket 16 | Type: AWS::S3::Bucket 17 | 18 | NotificationTopic: # Allows subscriptins for config updates 19 | Type: AWS::SNS::Topic 20 | 21 | DeliveryChannel: # Regional delivery channel 22 | Type: AWS::Config::DeliveryChannel 23 | Properties: 24 | S3BucketName: !Ref DataBucket 25 | SnsTopicARN: !Ref NotificationTopic 26 | 27 | ConfigRecorder: # Regional recorder 28 | Type: AWS::Config::ConfigurationRecorder 29 | Properties: 30 | Name: !Sub ${AWS::Region}-Config-Recorder 31 | RecordingGroup: 32 | IncludeGlobalResourceTypes: !If [IsMainRegion, true, false] 33 | AllSupported: true 34 | RoleARN: !GetAtt ConfigRole.Arn 35 | 36 | ConfigRole: # Role for config service, allowing access to S3 bucket and SNS 37 | Type: "AWS::IAM::Role" 38 | Properties: 39 | AssumeRolePolicyDocument: 40 | Version: "2012-10-17" 41 | Statement: 42 | - Effect: "Allow" 43 | Principal: 44 | Service: "config.amazonaws.com" 45 | Action: 46 | - "sts:AssumeRole" 47 | ManagedPolicyArns: 48 | - arn:aws:iam::aws:policy/service-role/AWSConfigRole 49 | Policies: 50 | - PolicyName: S3ConfigPolicy 51 | PolicyDocument: 52 | Version: '2012-10-17' 53 | Statement: 54 | Action: 55 | - s3:PutObject 56 | - s3:GetBucketAcl 57 | Effect: Allow 58 | Resource: 59 | - !GetAtt DataBucket.Arn 60 | - !Sub 61 | - "${BucketArn}/*" 62 | - {BucketArn: !GetAtt DataBucket.Arn} 63 | - PolicyName: SNSConfigPolicy 64 | PolicyDocument: 65 | Version: '2012-10-17' 66 | Statement: 67 | Action: 68 | - sns:Publish 69 | Effect: Allow 70 | Resource: 71 | - !Ref NotificationTopic -------------------------------------------------------------------------------- /governance/2-managed-rules/src/app.js: -------------------------------------------------------------------------------- 1 | const AWS = require('aws-sdk'); 2 | 3 | exports.lambdaHandler = async (event) => { 4 | console.log(JSON.stringify(event)) 5 | return true; 6 | } -------------------------------------------------------------------------------- /governance/2-managed-rules/template.yaml: -------------------------------------------------------------------------------- 1 | AWSTemplateFormatVersion: 2010-09-09 2 | Transform: AWS::Serverless-2016-10-31 3 | Description: Gevernance for Serverless managed rules 4 | 5 | 6 | Resources: 7 | ApiGWLoggingRule: # AWS Managed rule: enforces enabling of execution logging 8 | Type: AWS::Config::ConfigRule 9 | Properties: 10 | Description: Require API GW Logging 11 | # MaximumExecutionFrequency: One_Hour | Six_Hours | Three_Hours | Twelve_Hours | TwentyFour_Hours *Default 12 | Scope: 13 | ComplianceResourceTypes: 14 | - AWS::ApiGateway::Stage #API 15 | - AWS::ApiGatewayV2::Stage #HTTP API 16 | # ComplianceResourceId 17 | # TagKey: Environment 18 | # TagValue: Production 19 | Source: 20 | Owner: AWS 21 | SourceIdentifier: API_GW_EXECUTION_LOGGING_ENABLED 22 | 23 | S3NoPublicReadRule: # AWS Managed rule: enforces no S3 buckets with public read 24 | Type: AWS::Config::ConfigRule 25 | Properties: 26 | Description: S3 block public read 27 | Scope: 28 | ComplianceResourceTypes: 29 | - AWS::S3::Bucket 30 | Source: 31 | Owner: AWS 32 | SourceIdentifier: S3_BUCKET_PUBLIC_READ_PROHIBITED 33 | 34 | S3NoPublicWriteRule: # AWS Managed rule: enforces no S3 buckets with public write 35 | Type: AWS::Config::ConfigRule 36 | Properties: 37 | Description: S3 block public write 38 | Scope: 39 | ComplianceResourceTypes: 40 | - AWS::S3::Bucket 41 | Source: 42 | Owner: AWS 43 | SourceIdentifier: S3_BUCKET_PUBLIC_WRITE_PROHIBITED -------------------------------------------------------------------------------- /governance/3-custom-rules/example-events/3-rule-parameters.json: -------------------------------------------------------------------------------- 1 | { 2 | "resourceTypesArray": ["AWS::Lambda::Function"], 3 | "keyPath": "tracingConfig.mode", 4 | "acceptedValues": ["Active"] 5 | } -------------------------------------------------------------------------------- /governance/README.md: -------------------------------------------------------------------------------- 1 | # Governance for Serverless Applications using AWS Config and AWS SAM 2 | 3 | This group is broken into multiple parts. 4 | 5 | ## Part 1: Initial Setup 6 | This template configures AWS Config to record data in a specific region. The template declares a main region for recording global resources and can be installed in other regions within the same account as well. 7 | 8 | ## Part 2: AWS managed rules 9 | This template adds AWS managed rules to the currently existing AWS Config setup. These rules are sample rules with more found [here](https://docs.aws.amazon.com/config/latest/developerguide/managed-rules-by-aws-config.html). 10 | 11 | ## Part 3: Custom rules 12 | This template adds custom rules that evaluate reported configurations against custom logic through the use of an AWS Lambda function 13 | 14 | ## Part 4: Making the initial setup reusable 15 | This template uses pseudo parameters, intrinsic functions, and conditions to make the template reusable across regions and accounts. It also identifies an aggregate account capable of collecting data from sub-accounts 16 | 17 | ## Part 5: Putting it all together 18 | This template utilizes the AWS::Serverless::Application resource type to add the managed and custom rules to the config template as nestaed stacks. 19 | -------------------------------------------------------------------------------- /http-api-direct-integration/README.md: -------------------------------------------------------------------------------- 1 | 16 | 17 | # HTTP API direct integration example 18 | 19 | This template demonstrates using the new HTTP APIs direct integration to an Amazon SQS queue, a Kinesis Data Stream, and the default EventBridge bus. The following resources are built: 20 | * SQS Queue 21 | * Kinesis Data Stream 22 | * HTTP API 23 | * IAM Role for HTTP API 24 | * A Lambda function for testing. 25 | 26 | The *api.yaml* file contains the OpenAPI definition for the direct integration. 27 | 28 | ## Deployment 29 | From this folder run: 30 | ```bash 31 | sam deploy -g 32 | ``` 33 | 34 | ## Testing 35 | Update the '\' with your URL after deployment 36 | 37 | ### SQS 38 | ```bash 39 | curl --location --request POST '/sqs' \ 40 | --header 'Content-Type: application/json' \ 41 | --data-raw '{"MessageBody":"This is my message"}' 42 | ``` 43 | 44 | ### EventBridge 45 | ```bash 46 | curl --location --request POST '/eb' \ 47 | --header 'Content-Type: application/json' \ 48 | --data-raw '{"Detail": {"First Name": "My first name"}}' 49 | ``` 50 | 51 | ### Kinesis 52 | ```bash 53 | curl --location --request POST '/kinesis' \ 54 | --header 'Content-Type: application/json' \ 55 | --data-raw '{"Data": "Here is the data"}' 56 | ``` 57 | -------------------------------------------------------------------------------- /http-api-direct-integration/api.yaml: -------------------------------------------------------------------------------- 1 | openapi: "3.0.1" 2 | info: 3 | title: "DI All Integrations" 4 | paths: 5 | /sqs: 6 | post: 7 | responses: 8 | default: 9 | description: "SQS response" 10 | x-amazon-apigateway-integration: 11 | integrationSubtype: "SQS-SendMessage" 12 | credentials: 13 | Fn::GetAtt: [MyHttpApiRole, Arn] 14 | requestParameters: 15 | MessageBody: "$request.body.MessageBody" 16 | QueueUrl: 17 | Ref: MyQueue 18 | payloadFormatVersion: "1.0" 19 | type: "aws_proxy" 20 | connectionType: "INTERNET" 21 | /eb: 22 | post: 23 | responses: 24 | default: 25 | description: "EventBridge response" 26 | x-amazon-apigateway-integration: 27 | integrationSubtype: "EventBridge-PutEvents" 28 | credentials: 29 | Fn::GetAtt: [MyHttpApiRole, Arn] 30 | requestParameters: 31 | Detail: "$request.body.Detail" 32 | DetailType: MyDetailType 33 | Source: WebApp 34 | payloadFormatVersion: "1.0" 35 | type: "aws_proxy" 36 | connectionType: "INTERNET" 37 | /kinesis: 38 | post: 39 | responses: 40 | default: 41 | description: "Kinesis response" 42 | x-amazon-apigateway-integration: 43 | integrationSubtype: "Kinesis-PutRecord" 44 | credentials: 45 | Fn::GetAtt: [MyHttpApiRole, Arn] 46 | requestParameters: 47 | StreamName: 48 | Ref: MyStream 49 | Data: "$request.body.Data" 50 | PartitionKey: MyPartition 51 | payloadFormatVersion: "1.0" 52 | type: "aws_proxy" 53 | connectionType: "INTERNET" 54 | x-amazon-apigateway-importexport-version: "1.0" -------------------------------------------------------------------------------- /http-api-direct-integration/kinesis.json: -------------------------------------------------------------------------------- 1 | { 2 | "Records": [ 3 | { 4 | "kinesis": { 5 | "partitionKey": "partitionKey-03", 6 | "kinesisSchemaVersion": "1.0", 7 | "data": "SGVsbG8sIHRoaXMgaXMgYSB0ZXN0IDEyMy4=", 8 | "sequenceNumber": "49545115243490985018280067714973144582180062593244200961", 9 | "approximateArrivalTimestamp": 1428537600.0 10 | }, 11 | "eventSource": "aws:kinesis", 12 | "eventID": "shardId-000000000000:49545115243490985018280067714973144582180062593244200961", 13 | "invokeIdentityArn": "arn:aws:iam::EXAMPLE", 14 | "eventVersion": "1.0", 15 | "eventName": "aws:kinesis:record", 16 | "eventSourceARN": "arn:aws:kinesis:EXAMPLE", 17 | "awsRegion": "us-east-1" 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /http-api-direct-integration/src/app.js: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | // // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this 5 | // software and associated documentation files (the "Software"), to deal in the Software 6 | // without restriction, including without limitation the rights to use, copy, modify, 7 | // merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 8 | // permit persons to whom the Software is furnished to do so. 9 | // // 10 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 11 | // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 12 | // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 13 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 14 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 15 | // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 16 | 17 | exports.lambdaHandler = async (event) => { 18 | console.log(JSON.stringify(event)); 19 | return true 20 | } 21 | -------------------------------------------------------------------------------- /http-api-integrations-blog-example/README.md: -------------------------------------------------------------------------------- 1 | 16 | 17 | # HTTP API direct integration example 18 | 19 | Blog post: [Building storage-first serverless applications with HTTP APIs service integrations](https://aws.amazon.com/blogs/compute/building-storage-first-applications-with-http-apis-service-integrations/) 20 | 21 | This template demonstrates using the new HTTP APIs direct integration to an Amazon SQS queue. The following resources are built: 22 | * SQS Queue 23 | * HTTP API 24 | * IAM Role for HTTP API 25 | * A Lambda function for testing. 26 | 27 | The *api.yaml* file contains the OpenAPI definition for the direct integration. 28 | 29 | ## Deployement 30 | From this folder run: 31 | ```bash 32 | sam deploy -g 33 | ``` 34 | 35 | ## Testing 36 | Update the '\' with your URL after deployment 37 | 38 | ### SQS 39 | ```bash 40 | curl --location --request POST '/' \ 41 | --header 'Content-Type: application/json' \ 42 | --data-raw '{"MessageBody":"This is my message"}' 43 | ``` 44 | -------------------------------------------------------------------------------- /http-api-integrations-blog-example/api.yaml: -------------------------------------------------------------------------------- 1 | openapi: "3.0.1" 2 | info: 3 | title: "HTTP API to SQS" 4 | paths: 5 | /: 6 | post: 7 | responses: 8 | default: 9 | description: "SQS response" 10 | x-amazon-apigateway-integration: 11 | integrationSubtype: "SQS-SendMessage" 12 | credentials: 13 | Fn::GetAtt: [MyHttpApiRole, Arn] 14 | requestParameters: 15 | MessageBody: "$request.body.MessageBody" 16 | QueueUrl: 17 | Ref: MyQueue 18 | payloadFormatVersion: "1.0" 19 | type: "aws_proxy" 20 | connectionType: "INTERNET" 21 | x-amazon-apigateway-importexport-version: "1.0" -------------------------------------------------------------------------------- /http-api-integrations-blog-example/src/app.js: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | // // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this 5 | // software and associated documentation files (the "Software"), to deal in the Software 6 | // without restriction, including without limitation the rights to use, copy, modify, 7 | // merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 8 | // permit persons to whom the Software is furnished to do so. 9 | // // 10 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 11 | // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 12 | // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 13 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 14 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 15 | // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 16 | 17 | exports.lambdaHandler = async (event) => { 18 | console.log(JSON.stringify(event)); 19 | return true 20 | } 21 | -------------------------------------------------------------------------------- /http-api-integrations-blog-example/template.yaml: -------------------------------------------------------------------------------- 1 | AWSTemplateFormatVersion: '2010-09-09' 2 | Transform: AWS::Serverless-2016-10-31 3 | Description: HTTP API direct integrations 4 | 5 | Resources: 6 | MyQueue: 7 | Type: AWS::SQS::Queue 8 | 9 | MyHttpApi: 10 | Type: AWS::Serverless::HttpApi 11 | Properties: 12 | DefinitionBody: 13 | 'Fn::Transform': 14 | Name: 'AWS::Include' 15 | Parameters: 16 | Location: './api.yaml' 17 | 18 | MyHttpApiRole: 19 | Type: "AWS::IAM::Role" 20 | Properties: 21 | AssumeRolePolicyDocument: 22 | Version: "2012-10-17" 23 | Statement: 24 | - Effect: "Allow" 25 | Principal: 26 | Service: "apigateway.amazonaws.com" 27 | Action: 28 | - "sts:AssumeRole" 29 | Policies: 30 | - PolicyName: ApiDirectWriteToSQS 31 | PolicyDocument: 32 | Version: '2012-10-17' 33 | Statement: 34 | Action: 35 | - sqs:SendMessage 36 | Effect: Allow 37 | Resource: 38 | - !GetAtt MyQueue.Arn 39 | 40 | MyTriggeredLambda: 41 | Type: AWS::Serverless::Function 42 | Properties: 43 | CodeUri: src/ 44 | Handler: app.lambdaHandler 45 | Runtime: nodejs16.x 46 | Policies: 47 | - SQSPollerPolicy: 48 | QueueName: !GetAtt MyQueue.QueueName 49 | Events: 50 | SQSTrigger: 51 | Type: SQS 52 | Properties: 53 | Queue: !GetAtt MyQueue.Arn 54 | 55 | Outputs: 56 | ApiEndpoint: 57 | Description: "HTTP API endpoint URL" 58 | Value: !Sub "https://${MyHttpApi}.execute-api.${AWS::Region}.amazonaws.com" -------------------------------------------------------------------------------- /http-api-logging/README.md: -------------------------------------------------------------------------------- 1 | 16 | 17 | # HTTP APIs Access Logging 18 | Demonstrates adding access logging to HTTP APIs. -------------------------------------------------------------------------------- /http-api-logging/src/app.js: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | // // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this 5 | // software and associated documentation files (the "Software"), to deal in the Software 6 | // without restriction, including without limitation the rights to use, copy, modify, 7 | // merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 8 | // permit persons to whom the Software is furnished to do so. 9 | // // 10 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 11 | // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 12 | // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 13 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 14 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 15 | // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 16 | 17 | exports.lambdaHandler = async (event) => { 18 | console.log(JSON.stringify(event)) 19 | return JSON.stringify(event) 20 | }; -------------------------------------------------------------------------------- /http-api-logging/template.yaml: -------------------------------------------------------------------------------- 1 | AWSTemplateFormatVersion: '2010-09-09' 2 | Transform: AWS::Serverless-2016-10-31 3 | Description: Log Example 4 | 5 | Globals: 6 | Function: 7 | Timeout: 3 8 | 9 | Resources: 10 | MyHttpApi: 11 | Type: AWS::Serverless::HttpApi 12 | Properties: 13 | AccessLogSettings: 14 | DestinationArn: !GetAtt MyLogGroup.Arn 15 | Format: '{ "requestId":"$context.requestId", "ip": "$context.identity.sourceIp", "requestTime":"$context.requestTime", "httpMethod":"$context.httpMethod","routeKey":"$context.routeKey", "status":"$context.status","protocol":"$context.protocol", "responseLength":"$context.responseLength", "integrationError":"$context.integrationErrorMessage" }' 16 | CorsConfiguration: 17 | AllowMethods: 18 | - GET 19 | AllowOrigins: 20 | - http://localhost:8080 21 | 22 | MyLogGroup: 23 | Type: AWS::Logs::LogGroup 24 | 25 | HelloWorldFunction: 26 | Type: AWS::Serverless::Function 27 | Properties: 28 | CodeUri: src/ 29 | Handler: app.lambdaHandler 30 | Runtime: nodejs16.x 31 | Events: 32 | HelloWorld: 33 | Type: HttpApi 34 | Properties: 35 | Path: / 36 | Method: GET 37 | ApiId: !Ref MyHttpApi 38 | 39 | Outputs: 40 | HelloWorldApi: 41 | Description: "API Gateway endpoint URL for Prod stage for Hello World function" 42 | Value: !Sub "https://${MyHttpApi}.execute-api.${AWS::Region}.amazonaws.com" 43 | -------------------------------------------------------------------------------- /http-api/README.md: -------------------------------------------------------------------------------- 1 | 16 | 17 | # HTTP API with JWT 18 | 19 | Simple HTTP API SAM template with a non-authenticated endpoint and an authenticated endpoint using Amazon Cognito as the identity provider. 20 | 21 | ## Resources 22 | 23 | ### HttpApi 24 | Creates an HTTP API with a JWT authorizer 25 | 26 | ### LambdaFunction 27 | Creates an open route 28 | 29 | ### SingleAuthLambdaFunction 30 | Creates a route requiring authorization 31 | 32 | ### AdminLambdaFunction 33 | Creates a route requiring authorization and a custom scope "Admins-{Audience}" 34 | 35 | ### SULambdaFunction 36 | Creates a route requiring authorization and a custom scope "SU-{Audience}" 37 | 38 | ### BothLambdaFunction 39 | Creates a route requiring authorization and a custom scope "Admins-{Audience}" or "SU-{Audience}" 40 | 41 | ### CatchAllLambdaFunction 42 | Creates an unauthenticated $default route as a catchall -------------------------------------------------------------------------------- /http-api/src/app.js: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | // // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this 5 | // software and associated documentation files (the "Software"), to deal in the Software 6 | // without restriction, including without limitation the rights to use, copy, modify, 7 | // merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 8 | // permit persons to whom the Software is furnished to do so. 9 | // // 10 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 11 | // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 12 | // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 13 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 14 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 15 | // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 16 | 17 | exports.lambdaHandler = async (event) => JSON.stringify(event); 18 | -------------------------------------------------------------------------------- /http-api/src/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sample_app", 3 | "version": "1.0.0", 4 | "description": "Sample app for HTTP API", 5 | "main": "app.js", 6 | "repository": "https://github.com/singledigit/sam-template-examples/http-api", 7 | "author": "ejohnson", 8 | "license": "MIT", 9 | "dependencies": {}, 10 | "scripts": {}, 11 | "devDependencies": {} 12 | } -------------------------------------------------------------------------------- /kinesis-firehose/README.md: -------------------------------------------------------------------------------- 1 | 16 | 17 | # Kinesis-Firehose and Kinesis Analytics 18 | 19 | Amazon Kinesis Firehose SAM template for ingesting website access logs from Amazon API Gateway. The data is stored in a raw bucket, processed by a Lambda function, and then stored in a processed bucket as well. During the processing period, the data is also pushed to an Amazon DynamoDB table for real-time analytics. 20 | 21 | ## Resources according to data flow 22 | 23 | ### Firehose 24 | This is the initial delivery stream to ingest large amounts of data 25 | 26 | ### RawDataBucket 27 | This is the first step for the data. Raw data coming in to the Kinesis Dilvery Stream (Firehose) is first saved here in the format it comes in. 28 | 29 | ### ProcessFunction 30 | After saving the data to the RawDataBucket, Kinesis then triggers this Lambda for any custom processing 31 | 32 | ### ProcessedDataTable 33 | The process function pulls data and sends it to the ProcesseddataTable for immediate analytics 34 | 35 | ### ProcessedDataBucket 36 | This bucket stors the data in it's processed state 37 | 38 | ### KinesisAnalyticsApp 39 | Data is then sent to the KinesisAnalyticsApp for more in depth analysis and reporting 40 | 41 | ### KinesisAnalyticsOutput 42 | This output directs analyzed data from Kinesis Analytics to the Count Function 43 | 44 | ### CountFunction 45 | Count function takes the analyzed data and pushes it to the CountTable 46 | 47 | ### CountTable 48 | Stores the latest top vied links as a trend -------------------------------------------------------------------------------- /kinesis-firehose/src/count.js: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | // // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this 5 | // software and associated documentation files (the "Software"), to deal in the Software 6 | // without restriction, including without limitation the rights to use, copy, modify, 7 | // merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 8 | // permit persons to whom the Software is furnished to do so. 9 | // // 10 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 11 | // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 12 | // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 13 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 14 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 15 | // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 16 | 17 | const AWS = require('aws-sdk') 18 | const dc = new AWS.DynamoDB.DocumentClient() 19 | 20 | exports.handler = async (event) => { 21 | let items = [] 22 | let now = Date.now(); 23 | 24 | event.records.map(record => { 25 | let buff = new Buffer.from(record.data, 'base64'); 26 | let text = JSON.parse(buff.toString('utf-8')); 27 | 28 | let params = { 29 | TableName: process.env.TABLE_NAME, 30 | Key: { 31 | id: text.resourcePath.replace("/", '') 32 | }, 33 | AttributeUpdates: { 34 | count: { 35 | Action: "ADD", 36 | Value: text.LINK_COUNT 37 | }, 38 | updated: { 39 | Action: "PUT", 40 | Value: now 41 | } 42 | } 43 | } 44 | 45 | items.push(dc.update(params).promise()) 46 | }) 47 | 48 | try { 49 | await Promise.all(items) 50 | } catch (err) { 51 | console.log(err.message); 52 | } 53 | return null; 54 | } -------------------------------------------------------------------------------- /kinesis-firehose/src/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sample_app", 3 | "version": "1.0.0", 4 | "description": "Sample app for HTTP API", 5 | "main": "app.js", 6 | "repository": "https://github.com/singledigit/sam-template-examples/kinesis-firehose", 7 | "author": "ejohnson", 8 | "license": "MIT", 9 | "dependencies": {}, 10 | "scripts": {}, 11 | "devDependencies": {} 12 | } -------------------------------------------------------------------------------- /kinesis-firehose/src/process.js: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | // // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this 5 | // software and associated documentation files (the "Software"), to deal in the Software 6 | // without restriction, including without limitation the rights to use, copy, modify, 7 | // merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 8 | // permit persons to whom the Software is furnished to do so. 9 | // // 10 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 11 | // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 12 | // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 13 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 14 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 15 | // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 16 | 17 | const AWS = require('aws-sdk') 18 | const dc = new AWS.DynamoDB.DocumentClient() 19 | const TTL = 10 20 | 21 | exports.handler = async (event) => { 22 | let items = [] 23 | let now = Date.now(); 24 | let output = [] 25 | 26 | event.records.map(record => { 27 | let buff = new Buffer.from(record.data, 'base64'); 28 | let text = buff.toString('utf-8'); 29 | 30 | let params = { 31 | TableName: process.env.TABLE_NAME, 32 | Item: { 33 | id: record.recordId, 34 | createdTime: now, 35 | ttl: now + TTL * 60 * 1000, 36 | ...JSON.parse(text) 37 | }, 38 | ConditionExpression: 'attribute_not_exists(id)' 39 | } 40 | 41 | items.push(dc.put(params).promise()) 42 | output.push({ 43 | result: 'Ok', // Dropped | Ok | ProcessingFailed 44 | ...record 45 | }) 46 | }) 47 | 48 | try { 49 | await Promise.all(items) 50 | } catch (err) { 51 | console.log(err.message); 52 | } 53 | return { 54 | records: output 55 | } 56 | } -------------------------------------------------------------------------------- /lambda-layers/README.md: -------------------------------------------------------------------------------- 1 | 16 | 17 | # Lambda Layers 18 | This session talks through using Lambda layers externally and internally to the application. It also covers local development with Layers. 19 | 20 | ### Deploy layers repo first 21 | ``` 22 | cd layers-repo 23 | sam deploy -g 24 | ``` 25 | 26 | ### Test local app 27 | 1. update template with YOUR layerVersion ARNs 28 | 2. build and invoke 29 | ``` 30 | cd demo-app 31 | sam build && sam local invoke 32 | ``` 33 | -------------------------------------------------------------------------------- /lambda-layers/demo-app/.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/api/osx,node,linux,windows 3 | 4 | ### Linux ### 5 | *~ 6 | 7 | # temporary files which can be created if a process still has a handle open of a deleted file 8 | .fuse_hidden* 9 | 10 | # KDE directory preferences 11 | .directory 12 | 13 | # Linux trash folder which might appear on any partition or disk 14 | .Trash-* 15 | 16 | # .nfs files are created when an open file is removed but is still being accessed 17 | .nfs* 18 | 19 | ### Node ### 20 | # Logs 21 | logs 22 | *.log 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | 27 | # Runtime data 28 | pids 29 | *.pid 30 | *.seed 31 | *.pid.lock 32 | 33 | # Directory for instrumented libs generated by jscoverage/JSCover 34 | lib-cov 35 | 36 | # Coverage directory used by tools like istanbul 37 | coverage 38 | 39 | # nyc test coverage 40 | .nyc_output 41 | 42 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 43 | .grunt 44 | 45 | # Bower dependency directory (https://bower.io/) 46 | bower_components 47 | 48 | # node-waf configuration 49 | .lock-wscript 50 | 51 | # Compiled binary addons (http://nodejs.org/api/addons.html) 52 | build/Release 53 | 54 | # Dependency directories 55 | node_modules/ 56 | jspm_packages/ 57 | 58 | # Typescript v1 declaration files 59 | typings/ 60 | 61 | # Optional npm cache directory 62 | .npm 63 | 64 | # Optional eslint cache 65 | .eslintcache 66 | 67 | # Optional REPL history 68 | .node_repl_history 69 | 70 | # Output of 'npm pack' 71 | *.tgz 72 | 73 | # Yarn Integrity file 74 | .yarn-integrity 75 | 76 | # dotenv environment variables file 77 | .env 78 | 79 | 80 | ### OSX ### 81 | *.DS_Store 82 | .AppleDouble 83 | .LSOverride 84 | 85 | # Icon must end with two \r 86 | Icon 87 | 88 | # Thumbnails 89 | ._* 90 | 91 | # Files that might appear in the root of a volume 92 | .DocumentRevisions-V100 93 | .fseventsd 94 | .Spotlight-V100 95 | .TemporaryItems 96 | .Trashes 97 | .VolumeIcon.icns 98 | .com.apple.timemachine.donotpresent 99 | 100 | # Directories potentially created on remote AFP share 101 | .AppleDB 102 | .AppleDesktop 103 | Network Trash Folder 104 | Temporary Items 105 | .apdisk 106 | 107 | ### Windows ### 108 | # Windows thumbnail cache files 109 | Thumbs.db 110 | ehthumbs.db 111 | ehthumbs_vista.db 112 | 113 | # Folder config file 114 | Desktop.ini 115 | 116 | # Recycle Bin used on file shares 117 | $RECYCLE.BIN/ 118 | 119 | # Windows Installer files 120 | *.cab 121 | *.msi 122 | *.msm 123 | *.msp 124 | 125 | # Windows shortcuts 126 | *.lnk 127 | 128 | 129 | # End of https://www.gitignore.io/api/osx,node,linux,windows -------------------------------------------------------------------------------- /lambda-layers/demo-app/README.md: -------------------------------------------------------------------------------- 1 | # Demo App 2 | 3 | See [session readme](../README.md) -------------------------------------------------------------------------------- /lambda-layers/demo-app/events/event.json: -------------------------------------------------------------------------------- 1 | { 2 | "body": "{\"message\": \"hello world\"}", 3 | "resource": "/{proxy+}", 4 | "path": "/path/to/resource", 5 | "httpMethod": "POST", 6 | "isBase64Encoded": false, 7 | "queryStringParameters": { 8 | "foo": "bar" 9 | }, 10 | "pathParameters": { 11 | "proxy": "/path/to/resource" 12 | }, 13 | "stageVariables": { 14 | "baz": "qux" 15 | }, 16 | "headers": { 17 | "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", 18 | "Accept-Encoding": "gzip, deflate, sdch", 19 | "Accept-Language": "en-US,en;q=0.8", 20 | "Cache-Control": "max-age=0", 21 | "CloudFront-Forwarded-Proto": "https", 22 | "CloudFront-Is-Desktop-Viewer": "true", 23 | "CloudFront-Is-Mobile-Viewer": "false", 24 | "CloudFront-Is-SmartTV-Viewer": "false", 25 | "CloudFront-Is-Tablet-Viewer": "false", 26 | "CloudFront-Viewer-Country": "US", 27 | "Host": "1234567890.execute-api.us-east-1.amazonaws.com", 28 | "Upgrade-Insecure-Requests": "1", 29 | "User-Agent": "Custom User Agent String", 30 | "Via": "1.1 08f323deadbeefa7af34d5feb414ce27.cloudfront.net (CloudFront)", 31 | "X-Amz-Cf-Id": "cDehVQoZnx43VYQb9j2-nvCh-9z396Uhbp027Y2JvkCPNLmGJHqlaA==", 32 | "X-Forwarded-For": "127.0.0.1, 127.0.0.2", 33 | "X-Forwarded-Port": "443", 34 | "X-Forwarded-Proto": "https" 35 | }, 36 | "requestContext": { 37 | "accountId": "123456789012", 38 | "resourceId": "123456", 39 | "stage": "prod", 40 | "requestId": "c6af9ac6-7b61-11e6-9a41-93e8deadbeef", 41 | "requestTime": "09/Apr/2015:12:34:56 +0000", 42 | "requestTimeEpoch": 1428582896000, 43 | "identity": { 44 | "cognitoIdentityPoolId": null, 45 | "accountId": null, 46 | "cognitoIdentityId": null, 47 | "caller": null, 48 | "accessKey": null, 49 | "sourceIp": "127.0.0.1", 50 | "cognitoAuthenticationType": null, 51 | "cognitoAuthenticationProvider": null, 52 | "userArn": null, 53 | "userAgent": "Custom User Agent String", 54 | "user": null 55 | }, 56 | "path": "/prod/path/to/resource", 57 | "resourcePath": "/{proxy+}", 58 | "httpMethod": "POST", 59 | "apiId": "1234567890", 60 | "protocol": "HTTP/1.1" 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /lambda-layers/demo-app/hello-world/.npmignore: -------------------------------------------------------------------------------- 1 | tests/* 2 | -------------------------------------------------------------------------------- /lambda-layers/demo-app/hello-world/app.js: -------------------------------------------------------------------------------- 1 | const axios = require('axios') 2 | const url = 'http://checkip.amazonaws.com/'; 3 | const AWS = require('aws-sdk'); 4 | const moment = require('moment'); 5 | const numeral = require('numeral') 6 | let response; 7 | 8 | exports.lambdaHandler = async (event, context) => { 9 | try { 10 | const ret = await axios(url); 11 | response = { 12 | 'statusCode': 200, 13 | 'body': JSON.stringify({ 14 | message: 'Hi everyone!', 15 | sdkVersion: AWS.VERSION, 16 | location: ret.data.trim(), 17 | today: moment().format('MMMM Do YYYY, h:mm:ss a'), 18 | funNumber: numeral(1230974).format('($ 0.00 a)') 19 | }) 20 | } 21 | } catch (err) { 22 | console.log(err); 23 | return err; 24 | } 25 | 26 | return response 27 | }; 28 | -------------------------------------------------------------------------------- /lambda-layers/demo-app/hello-world/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hello-world", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "app.js", 6 | "directories": { 7 | "test": "tests" 8 | }, 9 | "scripts": { 10 | "test": "echo \"Error: no test specified\" && exit 1" 11 | }, 12 | "author": "Eric Johnson", 13 | "license": "ISC" 14 | } 15 | -------------------------------------------------------------------------------- /lambda-layers/demo-app/hello-world/tests/unit/test-handler.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const app = require('../../app.js'); 4 | const chai = require('chai'); 5 | const expect = chai.expect; 6 | var event, context; 7 | 8 | describe('Tests index', function () { 9 | it('verifies successful response', async () => { 10 | const result = await app.lambdaHandler(event, context) 11 | 12 | expect(result).to.be.an('object'); 13 | expect(result.statusCode).to.equal(200); 14 | expect(result.body).to.be.an('string'); 15 | 16 | let response = JSON.parse(result.body); 17 | 18 | expect(response).to.be.an('object'); 19 | expect(response.message).to.be.equal("hello world"); 20 | // expect(response.location).to.be.an("string"); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /lambda-layers/demo-app/layer/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "app-deps", 3 | "version": "1.0.0", 4 | "description": "app deps", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "Eric Johnson", 10 | "license": "ISC", 11 | "dependencies": { 12 | "numeral": "2.0.6" 13 | } 14 | } -------------------------------------------------------------------------------- /lambda-layers/demo-app/template.yaml: -------------------------------------------------------------------------------- 1 | AWSTemplateFormatVersion: '2010-09-09' 2 | Transform: AWS::Serverless-2016-10-31 3 | Description: Demo layer application 4 | 5 | Globals: 6 | Function: 7 | Timeout: 3 8 | Layers: 9 | # You need to exchange this for YOUR layers 10 | - 11 | - 12 | - !Ref AppLayer 13 | 14 | Resources: 15 | HelloWorldFunction: 16 | Type: AWS::Serverless::Function 17 | Properties: 18 | CodeUri: hello-world/ 19 | Handler: app.lambdaHandler 20 | Runtime: nodejs16.x 21 | Events: 22 | HelloWorld: 23 | Type: Api 24 | Properties: 25 | Path: / 26 | Method: get 27 | 28 | AppLayer: 29 | Type: AWS::Serverless::LayerVersion 30 | Properties: 31 | Description: app specific deps 32 | ContentUri: layer/ 33 | RetentionPolicy: Delete 34 | Metadata: 35 | BuildMethod: nodejs16.x 36 | 37 | Outputs: 38 | HelloWorldApi: 39 | Description: "API Gateway endpoint URL for Prod stage for Hello World function" 40 | Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/" 41 | HelloWorldFunction: 42 | Description: "Hello World Lambda Function ARN" 43 | Value: !GetAtt HelloWorldFunction.Arn 44 | HelloWorldFunctionIamRole: 45 | Description: "Implicit IAM Role created for Hello World function" 46 | Value: !GetAtt HelloWorldFunctionRole.Arn 47 | -------------------------------------------------------------------------------- /lambda-layers/layers-repo/node-base/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-base", 3 | "version": "1.0.0", 4 | "description": "base dependencies for serverless node apps", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "Eric Johnson", 10 | "license": "ISC", 11 | "dependencies": { 12 | "aws-sdk": "^2.1354.0", 13 | "axios": "^0.28.0" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /lambda-layers/layers-repo/node-utilities/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-base", 3 | "version": "1.0.0", 4 | "description": "base dependencies for serverless node apps", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "Eric Johnson", 10 | "license": "ISC", 11 | "dependencies": { 12 | "lodash": "^4.17.20", 13 | "moment": "^2.29.1" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /lambda-layers/layers-repo/python-base/requirements.txt: -------------------------------------------------------------------------------- 1 | boto3 2 | requests -------------------------------------------------------------------------------- /lambda-layers/layers-repo/template.yaml: -------------------------------------------------------------------------------- 1 | 2 | AWSTemplateFormatVersion: "2010-09-09" 3 | Transform: AWS::Serverless-2016-10-31 4 | Description: Template Repo 5 | 6 | Resources: 7 | NodeBaseLayer: 8 | Type: AWS::Serverless::LayerVersion 9 | Properties: 10 | # LayerName: NodeBaseLayer 11 | Description: Base layer for node apps (AWS-SDK, Axios) 12 | ContentUri: node-base/ 13 | # CompatibleRuntimes: 14 | # - nodejs16.x 15 | # LicenseInfo: 'MIT' 16 | RetentionPolicy: Retain 17 | Metadata: 18 | BuildMethod: nodejs16.x 19 | 20 | NodeBaseLayerPermission: 21 | Type: "AWS::Lambda::LayerVersionPermission" 22 | Properties: 23 | Action: lambda:GetLayerVersion 24 | LayerVersionArn: !Ref NodeBaseLayer 25 | Principal: !Ref AWS::AccountId 26 | 27 | NodeUtilitiesLayer: 28 | Type: AWS::Serverless::LayerVersion 29 | Properties: 30 | Description: Utilities layer for node apps (lodash, moment) 31 | ContentUri: node-utilities/ 32 | RetentionPolicy: Retain 33 | Metadata: 34 | BuildMethod: nodejs16.x 35 | 36 | NodeUtilitiesLayerPermission: 37 | Type: "AWS::Lambda::LayerVersionPermission" 38 | Properties: 39 | Action: lambda:GetLayerVersion 40 | LayerVersionArn: !Ref NodeUtilitiesLayer 41 | Principal: !Ref AWS::AccountId 42 | 43 | PythonBaseLayer: 44 | Type: AWS::Serverless::LayerVersion 45 | Properties: 46 | Description: Base layer for python apps (boto3, requests) 47 | ContentUri: python-base/ 48 | RetentionPolicy: Retain 49 | Metadata: 50 | BuildMethod: python3.8 51 | 52 | PythonBaseLayerPermission: 53 | Type: "AWS::Lambda::LayerVersionPermission" 54 | Properties: 55 | Action: lambda:GetLayerVersion 56 | LayerVersionArn: !Ref PythonBaseLayer 57 | Principal: !Ref AWS::AccountId 58 | 59 | Outputs: 60 | NodeBaseArn: 61 | Description: Layer Version ARN for node base 62 | Value: !Ref NodeBaseLayer 63 | NodeUtilitiesArn: 64 | Description: Layer Version ARN for utilities base 65 | Value: !Ref NodeUtilitiesLayer 66 | PythonBaseArn: 67 | Description: Layer Version ARN for python base 68 | Value: !Ref PythonBaseLayer -------------------------------------------------------------------------------- /multi-level-mapping/README.md: -------------------------------------------------------------------------------- 1 | # Amazon API Gateway multi level base path mapping 2 | 3 | -------------------------------------------------------------------------------- /multi-level-mapping/admin.yaml: -------------------------------------------------------------------------------- 1 | AWSTemplateFormatVersion: 2010-09-09 2 | Transform: AWS::Serverless-2016-10-31 3 | Description: Multi level base path mapping 4 | 5 | Parameters: 6 | DomainName: 7 | Type: String 8 | Description: Domian name for api 9 | 10 | Globals: 11 | Function: 12 | Handler: app.handler 13 | Runtime: nodejs16.x 14 | 15 | Resources: 16 | 17 | AdminMapping: #Creates the mapping for Admin 18 | Type: AWS::ApiGatewayV2::ApiMapping 19 | DependsOn: AdminAPIProdStage 20 | Properties: 21 | ApiId: !Ref AdminAPI 22 | ApiMappingKey: corp/admin 23 | DomainName: !Ref DomainName 24 | Stage: Prod 25 | 26 | AdminAPI: # Admin endpoint 27 | Type: AWS::Serverless::Api 28 | Properties: 29 | Description: Administrative API 30 | StageName: Prod 31 | EndpointConfiguration: 32 | Type: REGIONAL 33 | 34 | AdminFunction: # Administration function 35 | Type: AWS::Serverless::Function 36 | Properties: 37 | CodeUri: src/admin/ 38 | Events: 39 | CorpAdministration: 40 | Type: Api 41 | Properties: 42 | RestApiId: !Ref AdminAPI 43 | Method: GET 44 | Path: / -------------------------------------------------------------------------------- /multi-level-mapping/dadjokes.yaml: -------------------------------------------------------------------------------- 1 | AWSTemplateFormatVersion: 2010-09-09 2 | Transform: AWS::Serverless-2016-10-31 3 | Description: Dad jokes API 4 | 5 | Parameters: 6 | DomainName: 7 | Type: String 8 | Description: Domian name for api 9 | 10 | Resources: 11 | ReportingV1Mapping: # Creates the mapping for Reporting V1 12 | Type: AWS::ApiGatewayV2::ApiMapping 13 | DependsOn: ReportingAPIV1ApiGatewayDefaultStage 14 | Properties: 15 | ApiId: !Ref ReportingAPIV1 16 | ApiMappingKey: corp/jokes 17 | DomainName: !Ref DomainName 18 | Stage: $default 19 | 20 | ReportingAPIV1: # Reporting V1 endpoint 21 | Type: AWS::Serverless::HttpApi 22 | Properties: 23 | Description: Jokes API 24 | DefinitionBody: 25 | openapi: "3.0.1" 26 | info: 27 | title: "Corp Dad Jokes" 28 | paths: 29 | /: 30 | get: 31 | responses: 32 | default: 33 | description: "Default response for GET /" 34 | x-amazon-apigateway-integration: 35 | requestParameters: 36 | overwrite:header.Accept: "application/json" 37 | payloadFormatVersion: "1.0" 38 | type: "http_proxy" 39 | httpMethod: "ANY" 40 | uri: "https://icanhazdadjoke.com/" 41 | connectionType: "INTERNET" 42 | x-amazon-apigateway-importexport-version: "1.0" 43 | -------------------------------------------------------------------------------- /multi-level-mapping/reportingv1.yaml: -------------------------------------------------------------------------------- 1 | AWSTemplateFormatVersion: 2010-09-09 2 | Transform: AWS::Serverless-2016-10-31 3 | Description: Sales reporting application V1 4 | 5 | Parameters: 6 | DomainName: 7 | Type: String 8 | Description: Domian name for api 9 | 10 | Globals: 11 | Function: 12 | Handler: app.handler 13 | Runtime: nodejs16.x 14 | 15 | Resources: 16 | ReportingV1Mapping: # Creates the mapping for Reporting V1 17 | Type: AWS::ApiGatewayV2::ApiMapping 18 | DependsOn: ReportingAPIV1ApiGatewayDefaultStage 19 | Properties: 20 | ApiId: !Ref ReportingAPIV1 21 | ApiMappingKey: sales/reporting 22 | DomainName: !Ref DomainName 23 | Stage: $default 24 | 25 | ReportingAPIV1: # Reporting V1 endpoint 26 | Type: AWS::Serverless::HttpApi 27 | Properties: 28 | Description: Reporting API V1 29 | DisableExecuteApiEndpoint: true 30 | 31 | RegionalReporting: # Regional reporting function 32 | Type: AWS::Serverless::Function 33 | Properties: 34 | CodeUri: src/regional-reporting 35 | Events: 36 | RegionalReporting: 37 | Type: HttpApi 38 | Properties: 39 | ApiId: !Ref ReportingAPIV1 40 | Method: GET 41 | Path: /regional 42 | 43 | GlobalReportingV1: # Global reporting V1 function 44 | Type: AWS::Serverless::Function 45 | Properties: 46 | CodeUri: src/global-reportingv1 47 | Events: 48 | GlobalReportingV1: 49 | Type: HttpApi 50 | Properties: 51 | ApiId: !Ref ReportingAPIV1 52 | Method: GET 53 | Path: /global -------------------------------------------------------------------------------- /multi-level-mapping/reportingv2.yaml: -------------------------------------------------------------------------------- 1 | AWSTemplateFormatVersion: 2010-09-09 2 | Transform: AWS::Serverless-2016-10-31 3 | Description: Sales reporting application V1 4 | 5 | Parameters: 6 | DomainName: 7 | Type: String 8 | Description: Domian name for api 9 | 10 | Globals: 11 | Function: 12 | Handler: app.handler 13 | Runtime: nodejs16.x 14 | 15 | Resources: 16 | ReportingV2Mapping: # Creates the mapping for Reporting V1 17 | Type: AWS::ApiGatewayV2::ApiMapping 18 | DependsOn: ReportingAPIV2ApiGatewayDefaultStage 19 | Properties: 20 | ApiId: !Ref ReportingAPIV2 21 | ApiMappingKey: sales/reporting/v2 22 | DomainName: !Ref DomainName 23 | Stage: $default 24 | 25 | ReportingAPIV2: # Reporting V1 endpoint 26 | Type: AWS::Serverless::HttpApi 27 | Properties: 28 | Description: Reporting API V2 29 | DisableExecuteApiEndpoint: true 30 | 31 | GlobalReportingV2: # Global reporting V2 function 32 | Type: AWS::Serverless::Function 33 | Properties: 34 | CodeUri: src/global-reportingv2 35 | Events: 36 | GlobalReportingV2: 37 | Type: HttpApi 38 | Properties: 39 | ApiId: !Ref ReportingAPIV2 40 | Method: GET 41 | Path: /global -------------------------------------------------------------------------------- /multi-level-mapping/src/admin/app.js: -------------------------------------------------------------------------------- 1 | /*! Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | * SPDX-License-Identifier: MIT-0 3 | */ 4 | 5 | exports.handler = async (event) => { 6 | return { 7 | statusCode: 200, 8 | body: JSON.stringify({message:'You have reached the admin portal'}), 9 | }; 10 | }; -------------------------------------------------------------------------------- /multi-level-mapping/src/admin/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "admin", 3 | "version": "1.0.0", 4 | "description": "Admin endpoint", 5 | "main": "app.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "Eric Johnson", 10 | "license": "ISC" 11 | } 12 | -------------------------------------------------------------------------------- /multi-level-mapping/src/global-reportingv1/app.js: -------------------------------------------------------------------------------- 1 | /*! Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | * SPDX-License-Identifier: MIT-0 3 | */ 4 | 5 | exports.handler = async (event) => { 6 | return {message:'You have reached global reporting V1'} 7 | }; -------------------------------------------------------------------------------- /multi-level-mapping/src/global-reportingv1/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "admin", 3 | "version": "1.0.0", 4 | "description": "Admin endpoint", 5 | "main": "app.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "Eric Johnson", 10 | "license": "ISC" 11 | } 12 | -------------------------------------------------------------------------------- /multi-level-mapping/src/global-reportingv2/app.js: -------------------------------------------------------------------------------- 1 | /*! Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | * SPDX-License-Identifier: MIT-0 3 | */ 4 | 5 | exports.handler = async (event) => { 6 | return {message:'You have reached global reporting V2'} 7 | }; -------------------------------------------------------------------------------- /multi-level-mapping/src/global-reportingv2/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "admin", 3 | "version": "1.0.0", 4 | "description": "Admin endpoint", 5 | "main": "app.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "Eric Johnson", 10 | "license": "ISC" 11 | } 12 | -------------------------------------------------------------------------------- /multi-level-mapping/src/regional-reporting/app.js: -------------------------------------------------------------------------------- 1 | /*! Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | * SPDX-License-Identifier: MIT-0 3 | */ 4 | 5 | exports.handler = async (event) => { 6 | return {message:'You have reached regional reporting'} 7 | }; -------------------------------------------------------------------------------- /multi-level-mapping/src/regional-reporting/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "admin", 3 | "version": "1.0.0", 4 | "description": "Admin endpoint", 5 | "main": "app.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "Eric Johnson", 10 | "license": "ISC" 11 | } 12 | -------------------------------------------------------------------------------- /multi-level-mapping/template.yaml: -------------------------------------------------------------------------------- 1 | AWSTemplateFormatVersion: 2010-09-09 2 | Transform: AWS::Serverless-2016-10-31 3 | Description: Multi level base path mapping 4 | 5 | Parameters: 6 | DomainName: 7 | Type: String 8 | Description: Domian name for api 9 | ZoneId: 10 | Type: String 11 | Description: Zone ID 12 | Default: none 13 | CertArn: 14 | Type: String 15 | Description: Certificate ARN 16 | Default: none 17 | 18 | Resources: 19 | CustomDomainName: # Creates the domain name 20 | Type: AWS::ApiGatewayV2::DomainName 21 | Properties: 22 | DomainName: !Ref DomainName 23 | DomainNameConfigurations: 24 | - EndpointType: REGIONAL 25 | CertificateArn: !Ref CertArn 26 | 27 | DomainRecordSet: # Creates a record set in the hosted zone for the domain 28 | Type: AWS::Route53::RecordSet 29 | Properties: 30 | Name: !Ref DomainName 31 | HostedZoneId: !Ref ZoneId 32 | AliasTarget: 33 | DNSName: !GetAtt CustomDomainName.RegionalDomainName 34 | HostedZoneId: !GetAtt CustomDomainName.RegionalHostedZoneId 35 | Type: A 36 | 37 | ReportingV1App: 38 | Type: AWS::Serverless::Application 39 | Properties: 40 | Location: ./reportingv1.yaml 41 | Parameters: 42 | DomainName: !Ref CustomDomainName 43 | 44 | ReportingV2App: 45 | Type: AWS::Serverless::Application 46 | Properties: 47 | Location: ./reportingv2.yaml 48 | Parameters: 49 | DomainName: !Ref CustomDomainName 50 | 51 | AdminApp: 52 | Type: AWS::Serverless::Application 53 | Properties: 54 | Location: ./admin.yaml 55 | Parameters: 56 | DomainName: !Ref CustomDomainName -------------------------------------------------------------------------------- /node-webpack-custom-build/README.md: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | # Custom Build for Node apps 19 | This example demonstrates using AWS SAM cli custom build to add webpack to your build process. -------------------------------------------------------------------------------- /node-webpack-custom-build/data.json: -------------------------------------------------------------------------------- 1 | { 2 | "message":"SAM Rocks" 3 | } -------------------------------------------------------------------------------- /node-webpack-custom-build/events/event.json: -------------------------------------------------------------------------------- 1 | { 2 | "body": "{\"message\": \"hello world\"}", 3 | "resource": "/{proxy+}", 4 | "path": "/path/to/resource", 5 | "httpMethod": "POST", 6 | "isBase64Encoded": false, 7 | "queryStringParameters": { 8 | "foo": "bar" 9 | }, 10 | "pathParameters": { 11 | "proxy": "/path/to/resource" 12 | }, 13 | "stageVariables": { 14 | "baz": "qux" 15 | }, 16 | "headers": { 17 | "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", 18 | "Accept-Encoding": "gzip, deflate, sdch", 19 | "Accept-Language": "en-US,en;q=0.8", 20 | "Cache-Control": "max-age=0", 21 | "CloudFront-Forwarded-Proto": "https", 22 | "CloudFront-Is-Desktop-Viewer": "true", 23 | "CloudFront-Is-Mobile-Viewer": "false", 24 | "CloudFront-Is-SmartTV-Viewer": "false", 25 | "CloudFront-Is-Tablet-Viewer": "false", 26 | "CloudFront-Viewer-Country": "US", 27 | "Host": "1234567890.execute-api.us-east-1.amazonaws.com", 28 | "Upgrade-Insecure-Requests": "1", 29 | "User-Agent": "Custom User Agent String", 30 | "Via": "1.1 08f323deadbeefa7af34d5feb414ce27.cloudfront.net (CloudFront)", 31 | "X-Amz-Cf-Id": "cDehVQoZnx43VYQb9j2-nvCh-9z396Uhbp027Y2JvkCPNLmGJHqlaA==", 32 | "X-Forwarded-For": "127.0.0.1, 127.0.0.2", 33 | "X-Forwarded-Port": "443", 34 | "X-Forwarded-Proto": "https" 35 | }, 36 | "requestContext": { 37 | "accountId": "123456789012", 38 | "resourceId": "123456", 39 | "stage": "prod", 40 | "requestId": "c6af9ac6-7b61-11e6-9a41-93e8deadbeef", 41 | "requestTime": "09/Apr/2015:12:34:56 +0000", 42 | "requestTimeEpoch": 1428582896000, 43 | "identity": { 44 | "cognitoIdentityPoolId": null, 45 | "accountId": null, 46 | "cognitoIdentityId": null, 47 | "caller": null, 48 | "accessKey": null, 49 | "sourceIp": "127.0.0.1", 50 | "cognitoAuthenticationType": null, 51 | "cognitoAuthenticationProvider": null, 52 | "userArn": null, 53 | "userAgent": "Custom User Agent String", 54 | "user": null 55 | }, 56 | "path": "/prod/path/to/resource", 57 | "resourcePath": "/{proxy+}", 58 | "httpMethod": "POST", 59 | "apiId": "1234567890", 60 | "protocol": "HTTP/1.1" 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /node-webpack-custom-build/hello-world/.npmignore: -------------------------------------------------------------------------------- 1 | tests/* 2 | -------------------------------------------------------------------------------- /node-webpack-custom-build/hello-world/Makefile: -------------------------------------------------------------------------------- 1 | build-HelloWorldFunction: 2 | # echo $(PWD) >> $(ARTIFACTS_DIR)/../../info.txt 3 | # echo `pwd` >> $(ARTIFACTS_DIR)/../../info.txt 4 | webpack 5 | cp ./build/app.js $(ARTIFACTS_DIR)/app.js 6 | -------------------------------------------------------------------------------- /node-webpack-custom-build/hello-world/app.js: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | // // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this 5 | // software and associated documentation files (the "Software"), to deal in the Software 6 | // without restriction, including without limitation the rights to use, copy, modify, 7 | // merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 8 | // permit persons to whom the Software is furnished to do so. 9 | // // 10 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 11 | // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 12 | // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 13 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 14 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 15 | // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 16 | 17 | // const axios = require('axios') 18 | // const url = 'http://checkip.amazonaws.com/'; 19 | let response; 20 | // let d = require('../data.json'); 21 | 22 | /** 23 | * 24 | * Event doc: https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html#api-gateway-simple-proxy-for-lambda-input-format 25 | * @param {Object} event - API Gateway Lambda Proxy Input Format 26 | * 27 | * Context doc: https://docs.aws.amazon.com/lambda/latest/dg/nodejs-prog-model-context.html 28 | * @param {Object} context 29 | * 30 | * Return doc: https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html 31 | * @returns {Object} object - API Gateway Lambda Proxy Output Format 32 | * 33 | */ 34 | exports.lambdaHandler = async (event, context) => { 35 | try { 36 | // const ret = await axios(url); 37 | response = { 38 | 'statusCode': 200, 39 | 'body': JSON.stringify({ 40 | message: "hello world", 41 | // location: ret.data.trim() 42 | }) 43 | } 44 | } catch (err) { 45 | console.log(err); 46 | return err; 47 | } 48 | 49 | return response 50 | }; 51 | -------------------------------------------------------------------------------- /node-webpack-custom-build/hello-world/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hello_world", 3 | "version": "1.0.0", 4 | "description": "hello world sample for NodeJS", 5 | "main": "app.js", 6 | "repository": "https://github.com/awslabs/aws-sam-cli/tree/develop/samcli/local/init/templates/cookiecutter-aws-sam-hello-nodejs", 7 | "author": "SAM CLI", 8 | "license": "MIT", 9 | "dependencies": { 10 | "axios": "^0.28.0", 11 | "path": "^0.12.7" 12 | }, 13 | "scripts": { 14 | "test": "mocha tests/unit/" 15 | }, 16 | "devDependencies": { 17 | "chai": "^4.3.4", 18 | "mocha": "^9.0.0" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /node-webpack-custom-build/hello-world/tests/unit/test-handler.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const app = require('../../app.js'); 4 | const chai = require('chai'); 5 | const expect = chai.expect; 6 | var event, context; 7 | 8 | describe('Tests index', function () { 9 | it('verifies successful response', async () => { 10 | const result = await app.lambdaHandler(event, context) 11 | 12 | expect(result).to.be.an('object'); 13 | expect(result.statusCode).to.equal(200); 14 | expect(result.body).to.be.an('string'); 15 | 16 | let response = JSON.parse(result.body); 17 | 18 | expect(response).to.be.an('object'); 19 | expect(response.message).to.be.equal("hello world"); 20 | // expect(response.location).to.be.an("string"); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /node-webpack-custom-build/hello-world/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | const SRC_DIR = path.resolve(__dirname, './'); 4 | const OUT_DIR = path.resolve(__dirname, 'build'); 5 | 6 | const config = { 7 | entry: { 8 | app: path.resolve(SRC_DIR, 'app.js') 9 | }, 10 | // aws-sdk is already available in the Node.js Lambda environment 11 | // so it should not be included in function bundles 12 | externals: [ 13 | 'aws-sdk' 14 | ], 15 | output: { 16 | path: OUT_DIR, 17 | filename: '[name].js', 18 | library: '[name]', 19 | libraryTarget: 'umd' 20 | }, 21 | target: 'node' 22 | }; 23 | 24 | module.exports = config; -------------------------------------------------------------------------------- /node-webpack-custom-build/template.yaml: -------------------------------------------------------------------------------- 1 | AWSTemplateFormatVersion: '2010-09-09' 2 | Transform: AWS::Serverless-2016-10-31 3 | Description: > 4 | node-example 5 | 6 | Sample SAM Template for node-example 7 | 8 | # More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst 9 | Globals: 10 | Function: 11 | Timeout: 3 12 | 13 | Resources: 14 | HelloWorldFunction: 15 | Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction 16 | Properties: 17 | CodeUri: hello-world/ 18 | Handler: app.lambdaHandler 19 | Runtime: nodejs16.x 20 | Events: 21 | HelloWorld: 22 | Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api 23 | Properties: 24 | Path: /hello 25 | Method: get 26 | Metadata: 27 | BuildMethod: makefile 28 | 29 | Outputs: 30 | # ServerlessRestApi is an implicit API created out of Events key under Serverless::Function 31 | # Find out more about other implicit resources you can reference within SAM 32 | # https://github.com/awslabs/serverless-application-model/blob/master/docs/internals/generated_resources.rst#api 33 | HelloWorldApi: 34 | Description: "API Gateway endpoint URL for Prod stage for Hello World function" 35 | Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/hello/" 36 | HelloWorldFunction: 37 | Description: "Hello World Lambda Function ARN" 38 | Value: !GetAtt HelloWorldFunction.Arn 39 | HelloWorldFunctionIamRole: 40 | Description: "Implicit IAM Role created for Hello World function" 41 | Value: !GetAtt HelloWorldFunctionRole.Arn 42 | -------------------------------------------------------------------------------- /s12d/client/.env: -------------------------------------------------------------------------------- 1 | VUE_APP_NAME=s12d-shortener-beta 2 | VUE_APP_DOMAIN=https://beta.s12d.com 3 | VUE_APP_TITLE=Serverless Land URL Shortener (Beta) 4 | VUE_APP_API_ROOT=https://d42qgrz9nbfk8.cloudfront.net 5 | VUE_APP_AUTH_DOMAIN=https://s12d-shortener-beta-468083054740.auth.us-east-1.amazoncognito.com 6 | VUE_APP_CLIENT_ID=6hk11mbfh1inludkpkvns7bc6t 7 | VUE_DEFAULT_PAGE_SIZE=15 -------------------------------------------------------------------------------- /s12d/client/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | dist/ 3 | package-lock.json -------------------------------------------------------------------------------- /s12d/client/README.md: -------------------------------------------------------------------------------- 1 | 16 | 17 | # URL Shortener Client 18 | This application is a simple Vuejs application that interacts with the functionless url shortener. 19 | 20 | ![Personal access token scopes](../assets/client.png) 21 | 22 | ## Requirements 23 | * [Node](https://nodejs.org) 24 | * [Vue CLI](https://cli.vuejs.org/) 25 | 26 | If Node with NPM is already installed simply run. 27 | ``` 28 | npm install -g @vue/cli 29 | ``` 30 | 31 | ## Setup 32 | 33 | **In order for the local client to work for testing, be sure and set the *UseLocalClient* option when launching the backend stack** 34 | 35 | **The following commands need to be run from within the `client` folder.** 36 | 37 | ### 1. Update the environment variables in the `.env` file. 38 | The client needs some information about the backend. These values were output when you deployed the backend. If you need them again, simply run in your terminal: 39 | ``` 40 | aws cloudformation describe-stacks --stack-name URLShortener 41 | ``` 42 | Update and save the `.env` file. when you are done it should look "something" like this. 43 | 44 | ``` 45 | VUE_APP_NAME=shortener 46 | VUE_APP_API_ROOT=https://fd7c8be3rg.execute-api.us-west-2.amazonaws.com/Prod 47 | VUE_APP_AUTH_DOMAIN=https://shortener.auth.us-west-2.amazoncognito.com 48 | VUE_APP_CLIENT_ID=432p7npp8tf7a8pnb0hg5cbegl 49 | ``` 50 | 51 | ### 2. Install NPM dependencies 52 | 53 | ``` 54 | npm i 55 | ``` 56 | 57 | ### 3. Start the local server 58 | ``` 59 | npm run serve 60 | ``` 61 | 62 | ### 4. Open the webpage at [http://localhost:8080](http://localhost:8080) -------------------------------------------------------------------------------- /s12d/client/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/cli-plugin-babel/preset' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /s12d/client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "client2", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "serve": "vue-cli-service serve", 7 | "build": "vue-cli-service build", 8 | "lint": "vue-cli-service lint" 9 | }, 10 | "dependencies": { 11 | "axios": "^0.28.0" 12 | "bulma": "^0.9.3", 13 | "core-js": "^3.18.3", 14 | "moment": "^2.29.1", 15 | "nanoid": "^3.1.29", 16 | "numeral": "^2.0.6", 17 | "qrcode": "^1.4.4", 18 | "v-clipboard": "^2.2.3", 19 | "vue": "^2.6.11", 20 | "vue-qrcode": "^1.0.0", 21 | "vue-router": "^3.2.0", 22 | "vuex": "^3.4.0" 23 | }, 24 | "devDependencies": { 25 | "@vue/cli-plugin-babel": "^4.5.0", 26 | "@vue/cli-plugin-eslint": "^4.5.0", 27 | "@vue/cli-plugin-router": "^4.5.0", 28 | "@vue/cli-plugin-vuex": "^4.5.0", 29 | "@vue/cli-service": "^4.5.13", 30 | "babel-eslint": "^10.1.0", 31 | "eslint": "^6.7.2", 32 | "eslint-plugin-vue": "^6.2.2", 33 | "sass": "^1.26.5", 34 | "sass-loader": "^8.0.2", 35 | "vue-template-compiler": "^2.6.11" 36 | }, 37 | "eslintConfig": { 38 | "root": true, 39 | "env": { 40 | "node": true 41 | }, 42 | "extends": [ 43 | "plugin:vue/essential", 44 | "eslint:recommended" 45 | ], 46 | "rules": { 47 | "no-console": "off" 48 | }, 49 | "parserOptions": { 50 | "parser": "babel-eslint" 51 | } 52 | }, 53 | "browserslist": [ 54 | "> 1%", 55 | "last 2 versions" 56 | ] 57 | } 58 | -------------------------------------------------------------------------------- /s12d/client/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: [ 3 | require('tailwindcss'), 4 | require('autoprefixer'), 5 | ] 6 | } -------------------------------------------------------------------------------- /s12d/client/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/sessions-with-aws-sam/78ebaa71baadf092e13e635c1a0e89e7c49f802f/s12d/client/public/favicon.ico -------------------------------------------------------------------------------- /s12d/client/public/index.html: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | Serverless Land URL Shortner 25 | 26 | 27 | 28 | 31 |
32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /s12d/client/src/assets/tailwind.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; -------------------------------------------------------------------------------- /s12d/client/src/components/BarChart.js: -------------------------------------------------------------------------------- 1 | import { Bar, mixins } from 'vue-chartjs' 2 | const { reactiveProp } = mixins 3 | 4 | export default { 5 | extends: Bar, 6 | mixins: [reactiveProp], 7 | props: ['options'], 8 | mounted () { 9 | // this.chartData is created in the mixin. 10 | // If you want to pass options please create a local options object 11 | this.renderChart(this.chartData, this.options) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /s12d/client/src/main.js: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this 5 | // software and associated documentation files (the "Software"), to deal in the Software 6 | // without restriction, including without limitation the rights to use, copy, modify, 7 | // merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 8 | // permit persons to whom the Software is furnished to do so. 9 | // 10 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 11 | // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 12 | // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 13 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 14 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 15 | // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 16 | 17 | import Vue from 'vue' 18 | import App from './App.vue' 19 | import router from './router' 20 | import store from './store' 21 | import moment from 'moment' 22 | import numeral from 'numeral' 23 | import '@/assets/tailwind.css' 24 | import Clipboard from 'v-clipboard' 25 | 26 | Vue.use(Clipboard) 27 | Vue.config.productionTip = false 28 | 29 | // filters 30 | Vue.filter('formatDate', function (value) { 31 | return moment(value, 'DD/MMM/YYYY:HH:mm:ss Z').format("YYYY-MM-DD hh:mm a"); 32 | }) 33 | Vue.filter('formatNumber', function(value) { 34 | return numeral(value).format('0,0'); 35 | }) 36 | Vue.filter('formatPercent', function(value) { 37 | return numeral(value).format('0.00%'); 38 | }) 39 | 40 | new Vue({ 41 | router, 42 | store, 43 | render: h => h(App) 44 | }).$mount('#app') 45 | -------------------------------------------------------------------------------- /s12d/client/src/router/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import VueRouter from 'vue-router' 3 | import Dashboard from '../views/Dashboard.vue' 4 | import Details from '../views/Details.vue' 5 | import Create from '../views/Create.vue' 6 | 7 | Vue.use(VueRouter) 8 | 9 | const routes = [ 10 | { 11 | path: '/', 12 | name: 'dashboard', 13 | component: Dashboard 14 | }, 15 | { 16 | path: '/client/details/:linkId', 17 | name: 'details', 18 | component: Details 19 | }, 20 | { 21 | path: '/client/create', 22 | name: 'create', 23 | component: Create 24 | } 25 | ] 26 | 27 | const router = new VueRouter({ 28 | mode: 'history', 29 | base: process.env.BASE_URL, 30 | routes 31 | }) 32 | 33 | export default router 34 | -------------------------------------------------------------------------------- /s12d/client/src/store/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Vuex from 'vuex' 3 | import auth from './auth.js'; 4 | import links from './links.js'; 5 | 6 | Vue.use(Vuex) 7 | 8 | export default new Vuex.Store({ 9 | modules: { 10 | auth, 11 | links 12 | } 13 | }) -------------------------------------------------------------------------------- /s12d/client/vue.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | assetsDir:'s12dAssetsDirectory' 3 | } -------------------------------------------------------------------------------- /s12d/server/event.json: -------------------------------------------------------------------------------- 1 | { 2 | "Records": [ 3 | { 4 | "kinesis": { 5 | "kinesisSchemaVersion": "1.0", 6 | "partitionKey": "72d4c7ae-a478-4869-88ca-fec543085525", 7 | "sequenceNumber": "49612007989316666290349620221681111134766024667059716098", 8 | "data": "MTYwNDYxNzc4OS4wMDEJMjA5LjE2OS4xOTYuMTk3CTMwMQkvc2FtCVVTCg==", 9 | "approximateArrivalTimestamp": 1604617793.447 10 | }, 11 | "eventSource": "aws:kinesis", 12 | "eventVersion": "1.0", 13 | "eventID": "shardId-000000000000:49612007989316666290349620221681111134766024667059716098", 14 | "eventName": "aws:kinesis:record", 15 | "invokeIdentityArn": "arn:aws:iam::468083054740:role/s12d-url-shortener-beta-LoggingProcessorRole-176ZYVJFAO3A8", 16 | "awsRegion": "us-east-1", 17 | "eventSourceARN": "arn:aws:kinesis:us-east-1:468083054740:stream/s12d-url-shortener-beta-LoggingStream-15IOW5QPEWTEG" 18 | } 19 | ] 20 | } -------------------------------------------------------------------------------- /s12d/server/locals.json: -------------------------------------------------------------------------------- 1 | { 2 | "LoggingProcessor": { 3 | "TABLE_NAME": "s12d-url-shortener-beta-LinkTable-CYVRWVGAQG32" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /s12d/server/script.yml: -------------------------------------------------------------------------------- 1 | # Used with serverless-artillery for load testing. 2 | # `slsart invoke --stage us` or `slsart invoke --stage eu` 3 | # With proper credentials :) 4 | 5 | config: 6 | target: "http://beta.s12d.com" 7 | phases: 8 | - duration: 60 9 | arrivalRate: 20 10 | scenarios: 11 | - 12 | flow: 13 | - get: 14 | url: "/sam" 15 | followredirects: false 16 | - get: 17 | url: "/serverless" 18 | followredirects: false 19 | - get: 20 | url: "/broken" 21 | followredirects: false -------------------------------------------------------------------------------- /s12d/server/src/analytics/app.js: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | 4 | const AWS = require('aws-sdk') 5 | const doc = new AWS.DynamoDB.DocumentClient(); 6 | const table = process.env.TABLE_NAME; 7 | const blockedList = ['app', '', null, undefined] 8 | 9 | exports.handler = async (event) => { 10 | let updates = event.Records.map(record => { 11 | let dataString = Buffer.from(record.kinesis.data, "base64").toString("utf8"); 12 | let dataStringArr = dataString.split("\t"); 13 | let keyId = dataStringArr[3].replace(/\//, '') 14 | let country = dataStringArr[4].replace(/\n/, '') 15 | 16 | if (!blockedList.includes(keyId)) { 17 | return doc.update({ 18 | TableName: table, 19 | Key: { 20 | id: keyId 21 | }, 22 | UpdateExpression: `ADD #clicks :clicks, #regions.#country :clicks`, 23 | ConditionExpression: 'attribute_exists(id)', 24 | ExpressionAttributeNames: { 25 | '#clicks': 'clicks', 26 | '#regions': 'regions', 27 | '#country': country 28 | }, 29 | ExpressionAttributeValues: { 30 | ':clicks': 1 31 | } 32 | }).promise() 33 | } 34 | }) 35 | 36 | return Promise.all(updates).then(() => { 37 | return true 38 | }).catch(err => { 39 | if (err == 'ConditionalCheckFailedException: The conditional request failed') console.log(err); 40 | else throw err 41 | }) 42 | } -------------------------------------------------------------------------------- /s12d/server/src/login/app.js: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | 4 | exports.handler = async (event, context, callback) => { 5 | 6 | const userEmailDomain = event.request.userAttributes.email.split("@")[1]; 7 | const allowedDomain = 'amazon.com'; 8 | 9 | if (userEmailDomain === allowedDomain) { 10 | callback(null, event); 11 | } else { 12 | const error = new Error(`Sorry, you need an ${allowedDomain} email address to use s12d.com`); 13 | callback(error, event); 14 | } 15 | }; -------------------------------------------------------------------------------- /s12d/temp.json: -------------------------------------------------------------------------------- 1 | { 2 | "LastEvaluatedKey": "IiI=", 3 | "Items": [{ 4 | "id": "aws", 5 | "url": "https://aws.amazon.com", 6 | "timestamp": "28/Oct/2020:16:40:29 +0000", 7 | "owner": "ericdj@amazon.com", 8 | "clicks": "170306", 9 | "regions": { 10 | "DE": "13255" 11 | }, 12 | "US": "157051" 13 | } 14 | }, 15 | } 16 | / { "id":"serverless", "url": "https:/ / aws.amazon.com / serverless ", " 17 | timestamp ": " 18 | 27 / Oct / 2020: 22: 23: 17 + 0000 ", " 19 | owner ": " 20 | ericdj @amazon.com ", " 21 | clicks ": " 22 | 168939 ", " 23 | regions ": { " 24 | DE ":" 25 | 13177 " }, " 26 | US ":" 27 | 155762 " } }, } / { " 28 | id ":" 29 | sam ", " 30 | url ": " 31 | https: //aws.amazon.com/serverless/sam", "timestamp": "01/Nov/2020:05:10:55 +0000", "owner": "ericdj@amazon.com", "clicks": "167581", "regions": { "DE":"13345" }, "US":"154236" } } } / ]} -------------------------------------------------------------------------------- /safe-deploy/README.md: -------------------------------------------------------------------------------- 1 | 16 | 17 | # Safe deploys 18 | 19 | In this session we a Lambda functions for an application with a pre-traffic and post-traffic hook for testing. The application will rollback if any of the tests fail. We also demonstrate the use of a layer in the application. -------------------------------------------------------------------------------- /safe-deploy/layer/nodejs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aws-sdk-layer", 3 | "version": "1.0.0", 4 | "description": "AWS SDK Layer", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "Eric Johnson", 10 | "license": "MIT", 11 | "dependencies": { 12 | "aws-sdk": "^2.1354.0", 13 | "node-fetch": "^2.6.7" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /safe-deploy/src/base.js: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | // // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this 5 | // software and associated documentation files (the "Software"), to deal in the Software 6 | // without restriction, including without limitation the rights to use, copy, modify, 7 | // merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 8 | // permit persons to whom the Software is furnished to do so. 9 | // // 10 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 11 | // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 12 | // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 13 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 14 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 15 | // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 16 | 17 | exports.lambdaHandler = async (event) => { 18 | 19 | return {lambda: "one", preTest: true, postTest: true, code: 1} 20 | }; -------------------------------------------------------------------------------- /safe-deploy/src/hooks/basepost.js: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | // // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this 5 | // software and associated documentation files (the "Software"), to deal in the Software 6 | // without restriction, including without limitation the rights to use, copy, modify, 7 | // merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 8 | // permit persons to whom the Software is furnished to do so. 9 | // // 10 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 11 | // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 12 | // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 13 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 14 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 15 | // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 16 | 17 | const aws = require('aws-sdk'); 18 | const codedeploy = new aws.CodeDeploy(); 19 | 20 | exports.lambdaHandler = async (event) => { 21 | let status = 'Failed'; 22 | 23 | console.log("Entering PostTraffic Hook!"); 24 | console.log(JSON.stringify(event)); 25 | 26 | //Read the DeploymentId from the event payload. 27 | let deploymentId = event.DeploymentId; 28 | console.log("deploymentId=" + deploymentId); 29 | 30 | //Read the LifecycleEventHookExecutionId from the event payload 31 | let lifecycleEventHookExecutionId = event.LifecycleEventHookExecutionId; 32 | console.log("lifecycleEventHookExecutionId=" + lifecycleEventHookExecutionId); 33 | 34 | // // // // // // // // // // // // // 35 | // Your test logic here // // // // // 36 | // // // // // // // // // // // // // 37 | 38 | // Prepare the validation test results with the deploymentId and 39 | // the lifecycleEventHookExecutionId for AWS CodeDeploy. 40 | let params = { 41 | deploymentId: deploymentId, 42 | lifecycleEventHookExecutionId: lifecycleEventHookExecutionId, 43 | status: status // status can be 'Succeeded' or 'Failed' 44 | }; 45 | 46 | try { 47 | await codedeploy.putLifecycleEventHookExecutionStatus(params).promise(); 48 | console.log("putLifecycleEventHookExecutionStatus done. executionStatus=[" + params.status + "]"); 49 | return 'Validation test succeeded' 50 | } catch (err) { 51 | console.log("putLifecycleEventHookExecutionStatus ERROR: " + err); 52 | throw new Error('Validation test failed') 53 | } 54 | } -------------------------------------------------------------------------------- /sam-containers-demo-app/events/event.json: -------------------------------------------------------------------------------- 1 | { 2 | "body": "{\"message\": \"hello world\"}", 3 | "resource": "/{proxy+}", 4 | "path": "/path/to/resource", 5 | "httpMethod": "POST", 6 | "isBase64Encoded": false, 7 | "queryStringParameters": { 8 | "foo": "bar" 9 | }, 10 | "pathParameters": { 11 | "proxy": "/path/to/resource" 12 | }, 13 | "stageVariables": { 14 | "baz": "qux" 15 | }, 16 | "headers": { 17 | "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", 18 | "Accept-Encoding": "gzip, deflate, sdch", 19 | "Accept-Language": "en-US,en;q=0.8", 20 | "Cache-Control": "max-age=0", 21 | "CloudFront-Forwarded-Proto": "https", 22 | "CloudFront-Is-Desktop-Viewer": "true", 23 | "CloudFront-Is-Mobile-Viewer": "false", 24 | "CloudFront-Is-SmartTV-Viewer": "false", 25 | "CloudFront-Is-Tablet-Viewer": "false", 26 | "CloudFront-Viewer-Country": "US", 27 | "Host": "1234567890.execute-api.us-east-1.amazonaws.com", 28 | "Upgrade-Insecure-Requests": "1", 29 | "User-Agent": "Custom User Agent String", 30 | "Via": "1.1 08f323deadbeefa7af34d5feb414ce27.cloudfront.net (CloudFront)", 31 | "X-Amz-Cf-Id": "cDehVQoZnx43VYQb9j2-nvCh-9z396Uhbp027Y2JvkCPNLmGJHqlaA==", 32 | "X-Forwarded-For": "127.0.0.1, 127.0.0.2", 33 | "X-Forwarded-Port": "443", 34 | "X-Forwarded-Proto": "https" 35 | }, 36 | "requestContext": { 37 | "accountId": "123456789012", 38 | "resourceId": "123456", 39 | "stage": "prod", 40 | "requestId": "c6af9ac6-7b61-11e6-9a41-93e8deadbeef", 41 | "requestTime": "09/Apr/2015:12:34:56 +0000", 42 | "requestTimeEpoch": 1428582896000, 43 | "identity": { 44 | "cognitoIdentityPoolId": null, 45 | "accountId": null, 46 | "cognitoIdentityId": null, 47 | "caller": null, 48 | "accessKey": null, 49 | "sourceIp": "127.0.0.1", 50 | "cognitoAuthenticationType": null, 51 | "cognitoAuthenticationProvider": null, 52 | "userArn": null, 53 | "userAgent": "Custom User Agent String", 54 | "user": null 55 | }, 56 | "path": "/prod/path/to/resource", 57 | "resourcePath": "/{proxy+}", 58 | "httpMethod": "POST", 59 | "apiId": "1234567890", 60 | "protocol": "HTTP/1.1" 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /sam-containers-demo-app/goodbye-world/.npmignore: -------------------------------------------------------------------------------- 1 | tests/* 2 | -------------------------------------------------------------------------------- /sam-containers-demo-app/goodbye-world/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM public.ecr.aws/lambda/nodejs:12 2 | 3 | COPY app.js package.json ./ 4 | 5 | RUN npm install 6 | 7 | # Command can be overwritten by providing a different command in the template directly. 8 | CMD ["app.lambdaHandler"] 9 | -------------------------------------------------------------------------------- /sam-containers-demo-app/goodbye-world/app.js: -------------------------------------------------------------------------------- 1 | // const axios = require('axios') 2 | // const url = 'http://checkip.amazonaws.com/'; 3 | let response; 4 | 5 | /** 6 | * 7 | * Event doc: https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html#api-gateway-simple-proxy-for-lambda-input-format 8 | * @param {Object} event - API Gateway Lambda Proxy Input Format 9 | * 10 | * Context doc: https://docs.aws.amazon.com/lambda/latest/dg/nodejs-prog-model-context.html 11 | * @param {Object} context 12 | * 13 | * Return doc: https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html 14 | * @returns {Object} object - API Gateway Lambda Proxy Output Format 15 | * 16 | */ 17 | exports.lambdaHandler = async (event, context) => { 18 | try { 19 | // const ret = await axios(url); 20 | response = { 21 | 'statusCode': 200, 22 | 'body': JSON.stringify({ 23 | message: 'goodbye world', 24 | // location: ret.data.trim() 25 | }) 26 | } 27 | } catch (err) { 28 | console.log(err); 29 | return err; 30 | } 31 | 32 | return response 33 | }; 34 | -------------------------------------------------------------------------------- /sam-containers-demo-app/goodbye-world/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hello_world", 3 | "version": "1.0.0", 4 | "description": "hello world sample for NodeJS", 5 | "main": "app.js", 6 | "repository": "https://github.com/awslabs/aws-sam-cli/tree/develop/samcli/local/init/templates/cookiecutter-aws-sam-hello-nodejs", 7 | "author": "SAM CLI", 8 | "license": "MIT", 9 | "dependencies": { 10 | "axios": "^0.28.0" 11 | }, 12 | "scripts": { 13 | "test": "mocha tests/unit/" 14 | }, 15 | "devDependencies": { 16 | "chai": "^4.3.4", 17 | "mocha": "^9.0.0" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /sam-containers-demo-app/goodbye-world/tests/unit/test-handler.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const app = require('../../app.js'); 4 | const chai = require('chai'); 5 | const expect = chai.expect; 6 | var event, context; 7 | 8 | describe('Tests index', function () { 9 | it('verifies successful response', async () => { 10 | const result = await app.lambdaHandler(event, context) 11 | 12 | expect(result).to.be.an('object'); 13 | expect(result.statusCode).to.equal(200); 14 | expect(result.body).to.be.an('string'); 15 | 16 | let response = JSON.parse(result.body); 17 | 18 | expect(response).to.be.an('object'); 19 | expect(response.message).to.be.equal("hello world"); 20 | // expect(response.location).to.be.an("string"); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /sam-containers-demo-app/hello-world/.npmignore: -------------------------------------------------------------------------------- 1 | tests/* 2 | -------------------------------------------------------------------------------- /sam-containers-demo-app/hello-world/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM public.ecr.aws/lambda/nodejs:12 2 | 3 | COPY app.js package.json ./ 4 | 5 | RUN npm install 6 | 7 | # Command can be overwritten by providing a different command in the template directly. 8 | CMD ["app.lambdaHandler"] 9 | -------------------------------------------------------------------------------- /sam-containers-demo-app/hello-world/app.js: -------------------------------------------------------------------------------- 1 | // const axios = require('axios') 2 | // const url = 'http://checkip.amazonaws.com/'; 3 | let response; 4 | 5 | /** 6 | * 7 | * Event doc: https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html#api-gateway-simple-proxy-for-lambda-input-format 8 | * @param {Object} event - API Gateway Lambda Proxy Input Format 9 | * 10 | * Context doc: https://docs.aws.amazon.com/lambda/latest/dg/nodejs-prog-model-context.html 11 | * @param {Object} context 12 | * 13 | * Return doc: https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html 14 | * @returns {Object} object - API Gateway Lambda Proxy Output Format 15 | * 16 | */ 17 | exports.lambdaHandler = async (event, context) => { 18 | try { 19 | // const ret = await axios(url); 20 | response = { 21 | 'statusCode': 200, 22 | 'body': JSON.stringify({ 23 | message: 'hello world', 24 | // location: ret.data.trim() 25 | }) 26 | } 27 | } catch (err) { 28 | console.log(err); 29 | return err; 30 | } 31 | 32 | return response 33 | }; 34 | -------------------------------------------------------------------------------- /sam-containers-demo-app/hello-world/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hello_world", 3 | "version": "1.0.0", 4 | "description": "hello world sample for NodeJS", 5 | "main": "app.js", 6 | "repository": "https://github.com/awslabs/aws-sam-cli/tree/develop/samcli/local/init/templates/cookiecutter-aws-sam-hello-nodejs", 7 | "author": "SAM CLI", 8 | "license": "MIT", 9 | "dependencies": { 10 | "axios": "^0.28.0" 11 | }, 12 | "scripts": { 13 | "test": "mocha tests/unit/" 14 | }, 15 | "devDependencies": { 16 | "chai": "^4.3.4", 17 | "mocha": "^9.0.0" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /sam-containers-demo-app/hello-world/tests/unit/test-handler.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const app = require('../../app.js'); 4 | const chai = require('chai'); 5 | const expect = chai.expect; 6 | var event, context; 7 | 8 | describe('Tests index', function () { 9 | it('verifies successful response', async () => { 10 | const result = await app.lambdaHandler(event, context) 11 | 12 | expect(result).to.be.an('object'); 13 | expect(result.statusCode).to.equal(200); 14 | expect(result.body).to.be.an('string'); 15 | 16 | let response = JSON.parse(result.body); 17 | 18 | expect(response).to.be.an('object'); 19 | expect(response.message).to.be.equal("hello world"); 20 | // expect(response.location).to.be.an("string"); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /sam-containers-demo-app/template.yaml: -------------------------------------------------------------------------------- 1 | AWSTemplateFormatVersion: '2010-09-09' 2 | Transform: AWS::Serverless-2016-10-31 3 | Description: > 4 | sam-containers-demo-app 5 | 6 | Sample SAM Template for sam-containers-demo-app 7 | 8 | # More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst 9 | Globals: 10 | Function: 11 | Timeout: 3 12 | 13 | Resources: 14 | HelloWorldFunction: 15 | Type: AWS::Serverless::Function 16 | Properties: 17 | PackageType: Image 18 | Events: 19 | HelloWorld: 20 | Type: Api 21 | Properties: 22 | Path: /hello 23 | Method: get 24 | Metadata: 25 | DockerTag: nodejs16.x-v1 26 | DockerContext: ./hello-world 27 | Dockerfile: Dockerfile 28 | 29 | GoodbyeWorldFunction: 30 | Type: AWS::Serverless::Function 31 | Properties: 32 | PackageType: Image 33 | Events: 34 | GoodbyeWorld: 35 | Type: Api 36 | Properties: 37 | Path: /goodbye 38 | Method: get 39 | Metadata: 40 | DockerTag: nodejs16.x-v1 41 | DockerContext: ./goodbye-world 42 | Dockerfile: Dockerfile 43 | 44 | Outputs: 45 | # ServerlessRestApi is an implicit API created out of Events key under Serverless::Function 46 | # Find out more about other implicit resources you can reference within SAM 47 | # https://github.com/awslabs/serverless-application-model/blob/master/docs/internals/generated_resources.rst#api 48 | HelloWorldApi: 49 | Description: "API Gateway endpoint URL for Prod stage for Hello World function" 50 | Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/hello/" 51 | GoodbyeWorldApi: 52 | Description: "API Gateway endpoint URL for Prod stage for Goodbye World function" 53 | Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/goodbye/" 54 | HelloWorldFunction: 55 | Description: "Hello World Lambda Function ARN" 56 | Value: !GetAtt HelloWorldFunction.Arn 57 | HelloWorldFunctionIamRole: 58 | Description: "Implicit IAM Role created for Hello World function" 59 | Value: !GetAtt HelloWorldFunctionRole.Arn 60 | -------------------------------------------------------------------------------- /sam-or-cdk/cdk/.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 | asset.* 10 | template.yaml 11 | event.json 12 | locals.json -------------------------------------------------------------------------------- /sam-or-cdk/cdk/.npmignore: -------------------------------------------------------------------------------- 1 | *.ts 2 | !*.d.ts 3 | 4 | # CDK asset staging directory 5 | .cdk.staging 6 | cdk.out 7 | -------------------------------------------------------------------------------- /sam-or-cdk/cdk/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 | -------------------------------------------------------------------------------- /sam-or-cdk/cdk/bin/signedurl.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import 'source-map-support/register'; 3 | import * as cdk from '@aws-cdk/core'; 4 | import { CDKSignedurlStack } from '../lib/signedurl-stack'; 5 | 6 | const app = new cdk.App(); 7 | new CDKSignedurlStack(app, 'CDKSignedurlStack', { 8 | description: "CDK Signed Url Generator" 9 | }); 10 | -------------------------------------------------------------------------------- /sam-or-cdk/cdk/cdk.json: -------------------------------------------------------------------------------- 1 | { 2 | "app": "npx ts-node --prefer-ts-exts bin/signedurl.ts", 3 | "context": { 4 | "@aws-cdk/core:enableStackNameDuplicates": "true", 5 | "aws-cdk:enableDiffNoFail": "true", 6 | "@aws-cdk/core:stackRelativeExports": "true", 7 | "@aws-cdk/aws-ecr-assets:dockerIgnoreSupport": true, 8 | "@aws-cdk/aws-secretsmanager:parseOwnedSecretName": true, 9 | "@aws-cdk/aws-kms:defaultKeyPolicies": true 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /sam-or-cdk/cdk/events/fetchUrl.json: -------------------------------------------------------------------------------- 1 | { 2 | "pathParameters": { 3 | "id": "aNNUabohoePKM4JUnTXxw" 4 | } 5 | } -------------------------------------------------------------------------------- /sam-or-cdk/cdk/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | roots: ['/test'], 3 | testMatch: ['**/*.test.ts'], 4 | transform: { 5 | '^.+\\.tsx?$': 'ts-jest' 6 | } 7 | }; 8 | -------------------------------------------------------------------------------- /sam-or-cdk/cdk/lambda/downloadSigner.ts: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | 4 | import {S3} from "aws-sdk"; 5 | import {generate} from 'short-uuid' 6 | const s3 = new S3() 7 | 8 | exports.handler = async function (event: any) { 9 | const now = new Date() 10 | const shortId = generate(); 11 | 12 | let params = { 13 | Bucket: process.env.STORAGE_BUCKET, 14 | Key: event.key, 15 | Expires: event.expiration || (15*60) 16 | } 17 | 18 | try { 19 | let results = await s3.getSignedUrlPromise('getObject', params) 20 | return { 21 | id: shortId, 22 | signedUrl: results, 23 | ttl: (Math.round(now.getTime() / 1000) + params.Expires).toString() 24 | }; 25 | } catch (error) { 26 | throw new Error(error.message) 27 | } 28 | } -------------------------------------------------------------------------------- /sam-or-cdk/cdk/lambda/fetchShortUrl.ts: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | 4 | import { DynamoDB } from "aws-sdk" 5 | const dc = new DynamoDB.DocumentClient() 6 | 7 | exports.handler = async function (event: any) { 8 | let response = { 9 | "isBase64Encoded": false, 10 | "statusCode": 200, 11 | "body": JSON.stringify({"Mesaage": "Signed URL not found or expired"}), 12 | "headers": { 13 | "Content-Type": "application/json", 14 | "Location": "" 15 | } 16 | } 17 | 18 | let params: any = { 19 | TableName: process.env.URL_TABLE, 20 | Key:{ 21 | id: event.pathParameters.id, 22 | } 23 | } 24 | 25 | try{ 26 | let data = await dc.get(params).promise() 27 | 28 | if(Object.keys(data).length > 0){ 29 | response.statusCode = 302; 30 | response.headers.Location = data.Item!.signedUrl, 31 | response.body = "" 32 | } 33 | return response 34 | } catch(error) { 35 | throw new Error(error.message) 36 | } 37 | } -------------------------------------------------------------------------------- /sam-or-cdk/cdk/lambda/uploadSigner.ts: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | 4 | import {S3} from "aws-sdk"; 5 | const s3 = new S3() 6 | 7 | exports.handler = async function (event: any) { 8 | 9 | let params = { 10 | Bucket: process.env.STORAGE_BUCKET, 11 | Key: event.key, 12 | Expires: event.expiration || (5*60), 13 | ContentType: event.contentType, 14 | } 15 | 16 | try { 17 | let results = await s3.getSignedUrlPromise('putObject', params) 18 | return { 19 | signedUrl: results 20 | }; 21 | } catch (error) { 22 | throw new Error(error.message) 23 | } 24 | } -------------------------------------------------------------------------------- /sam-or-cdk/cdk/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "signedurl", 3 | "version": "0.1.0", 4 | "bin": { 5 | "signedurl": "bin/signedurl.js" 6 | }, 7 | "scripts": { 8 | "build": "tsc", 9 | "watch": "tsc -w", 10 | "test": "jest", 11 | "cdk": "cdk" 12 | }, 13 | "devDependencies": { 14 | "@aws-cdk/assert": "1.102.0", 15 | "@types/jest": "^26.0.23", 16 | "@types/node": "15.0.2", 17 | "aws-cdk": "1.102.0", 18 | "esbuild": "^0.11.20", 19 | "jest": "^26.6.3", 20 | "ts-jest": "^26.5.6", 21 | "ts-node": "^9.1.1", 22 | "typescript": "~4.2.4" 23 | }, 24 | "dependencies": { 25 | "@aws-cdk/aws-apigatewayv2": "^1.102.0", 26 | "@aws-cdk/aws-apigatewayv2-integrations": "^1.102.0", 27 | "@aws-cdk/aws-cloudtrail": "^1.102.0", 28 | "@aws-cdk/aws-dynamodb": "^1.102.0", 29 | "@aws-cdk/aws-events": "^1.102.0", 30 | "@aws-cdk/aws-events-targets": "^1.102.0", 31 | "@aws-cdk/aws-iam": "^1.102.0", 32 | "@aws-cdk/aws-lambda": "^1.102.0", 33 | "@aws-cdk/aws-lambda-nodejs": "^1.102.0", 34 | "@aws-cdk/aws-s3": "^1.102.0", 35 | "@aws-cdk/aws-stepfunctions": "^1.102.0", 36 | "@aws-cdk/aws-stepfunctions-tasks": "^1.102.0", 37 | "@aws-cdk/core": "1.102.0", 38 | "@aws-sdk/client-s3": "^3.14.0", 39 | "aws-sdk": "^2.904.0", 40 | "short-unique-id": "^4.3.3", 41 | "short-uuid": "^4.1.0", 42 | "source-map-support": "^0.5.19" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /sam-or-cdk/cdk/test/signedurl.test.ts: -------------------------------------------------------------------------------- 1 | import { expect as expectCDK, matchTemplate, MatchStyle } from '@aws-cdk/assert'; 2 | import * as cdk from '@aws-cdk/core'; 3 | import * as Signedurl from '../lib/signedurl-stack'; 4 | 5 | test('Empty Stack', () => { 6 | const app = new cdk.App(); 7 | // WHEN 8 | const stack = new Signedurl.CDKSignedurlStack(app, 'MyTestStack'); 9 | // THEN 10 | expectCDK(stack).to(matchTemplate({ 11 | "Resources": {} 12 | }, MatchStyle.EXACT)) 13 | }); 14 | -------------------------------------------------------------------------------- /sam-or-cdk/cdk/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 | -------------------------------------------------------------------------------- /sam-or-cdk/sam/events/event.json: -------------------------------------------------------------------------------- 1 | { 2 | "key": "me.jpg", 3 | "contentType": "image/jpeg", 4 | "expiration": 600 5 | } -------------------------------------------------------------------------------- /sam-or-cdk/sam/events/fetchUrl.json: -------------------------------------------------------------------------------- 1 | { 2 | "pathParameters": { 3 | "id": "fMRY9MEEXtLk5cCaUXTn3H" 4 | } 5 | } -------------------------------------------------------------------------------- /sam-or-cdk/sam/lambda/downloadSigner/.npmignore: -------------------------------------------------------------------------------- 1 | tests/* 2 | -------------------------------------------------------------------------------- /sam-or-cdk/sam/lambda/downloadSigner/app.js: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | 4 | const AWS = require('aws-sdk'); 5 | const s3 = new AWS.S3(); 6 | const short = require('short-uuid'); 7 | 8 | exports.handler = async function (event) { 9 | const now = new Date(); 10 | const shortId = short.generate(); 11 | 12 | let params = { 13 | Bucket: process.env.STORAGE_BUCKET, 14 | Key: event.key, 15 | Expires: event.expiration || (15*60) 16 | } 17 | 18 | try { 19 | let results = await s3.getSignedUrlPromise('getObject', params); 20 | return { 21 | id: shortId, 22 | signedUrl: results, 23 | ttl: (Math.round(now.getTime() / 1000) + params.Expires).toString() 24 | }; 25 | } catch (error) { 26 | throw new Error(error.message); 27 | } 28 | } -------------------------------------------------------------------------------- /sam-or-cdk/sam/lambda/downloadSigner/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hello_world", 3 | "version": "1.0.0", 4 | "description": "hello world sample for NodeJS", 5 | "main": "app.js", 6 | "repository": "https://github.com/awslabs/aws-sam-cli/tree/develop/samcli/local/init/templates/cookiecutter-aws-sam-hello-nodejs", 7 | "author": "SAM CLI", 8 | "license": "MIT", 9 | "dependencies": { 10 | "short-uuid": "^4.1.0" 11 | }, 12 | "scripts": { 13 | "test": "mocha tests/unit/" 14 | }, 15 | "devDependencies": { 16 | "chai": "^4.2.0", 17 | "mocha": "^8.2.1" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /sam-or-cdk/sam/lambda/downloadSigner/tests/unit/test-handler.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const app = require('../../app.js'); 4 | const chai = require('chai'); 5 | const expect = chai.expect; 6 | var event, context; 7 | 8 | describe('Tests index', function () { 9 | it('verifies successful response', async () => { 10 | const result = await app.lambdaHandler(event, context) 11 | 12 | expect(result).to.be.an('object'); 13 | expect(result.statusCode).to.equal(200); 14 | expect(result.body).to.be.an('string'); 15 | 16 | let response = JSON.parse(result.body); 17 | 18 | expect(response).to.be.an('object'); 19 | expect(response.message).to.be.equal("hello world"); 20 | // expect(response.location).to.be.an("string"); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /sam-or-cdk/sam/lambda/fetchShortUrl/.npmignore: -------------------------------------------------------------------------------- 1 | tests/* 2 | -------------------------------------------------------------------------------- /sam-or-cdk/sam/lambda/fetchShortUrl/app.js: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | 4 | const AWS = require('aws-sdk') 5 | const dc = new AWS.DynamoDB.DocumentClient() 6 | 7 | exports.handler = async function (event) { 8 | let response = { 9 | "isBase64Encoded": false, 10 | "statusCode": 200, 11 | "body": JSON.stringify({"Message": "Signed URL not found or expired"}), 12 | "headers": { 13 | "Content-Type": "application/json", 14 | "Location": "" 15 | } 16 | } 17 | 18 | let params = { 19 | TableName: process.env.URL_TABLE, 20 | Key:{ 21 | id: event.pathParameters.id, 22 | } 23 | } 24 | 25 | try{ 26 | let data = await dc.get(params).promise() 27 | 28 | if(Object.keys(data).length > 0){ 29 | response.statusCode = 302; 30 | response.headers.Location = data.Item.signedUrl, 31 | response.body = "" 32 | } 33 | return response 34 | } catch(error) { 35 | throw new Error(error.message) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /sam-or-cdk/sam/lambda/fetchShortUrl/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hello_world", 3 | "version": "1.0.0", 4 | "description": "hello world sample for NodeJS", 5 | "main": "app.js", 6 | "repository": "https://github.com/awslabs/aws-sam-cli/tree/develop/samcli/local/init/templates/cookiecutter-aws-sam-hello-nodejs", 7 | "author": "SAM CLI", 8 | "license": "MIT", 9 | "dependencies": {}, 10 | "scripts": { 11 | "test": "mocha tests/unit/" 12 | }, 13 | "devDependencies": { 14 | "chai": "^4.2.0", 15 | "mocha": "^8.2.1" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /sam-or-cdk/sam/lambda/fetchShortUrl/tests/unit/test-handler.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const app = require('../../app.js'); 4 | const chai = require('chai'); 5 | const expect = chai.expect; 6 | var event, context; 7 | 8 | describe('Tests index', function () { 9 | it('verifies successful response', async () => { 10 | const result = await app.lambdaHandler(event, context) 11 | 12 | expect(result).to.be.an('object'); 13 | expect(result.statusCode).to.equal(200); 14 | expect(result.body).to.be.an('string'); 15 | 16 | let response = JSON.parse(result.body); 17 | 18 | expect(response).to.be.an('object'); 19 | expect(response.message).to.be.equal("hello world"); 20 | // expect(response.location).to.be.an("string"); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /sam-or-cdk/sam/lambda/uploadSigner/.npmignore: -------------------------------------------------------------------------------- 1 | tests/* 2 | -------------------------------------------------------------------------------- /sam-or-cdk/sam/lambda/uploadSigner/app.js: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | 4 | const AWS = require('aws-sdk'); 5 | const s3 = new AWS.S3() 6 | 7 | exports.handler = async function (event) { 8 | 9 | let params = { 10 | Bucket: process.env.STORAGE_BUCKET, 11 | Key: event.key, 12 | Expires: event.expiration || (5*60), 13 | ContentType: event.contentType, 14 | } 15 | 16 | try { 17 | let results = await s3.getSignedUrlPromise('putObject', params) 18 | return { 19 | signedUrl: results 20 | }; 21 | } catch (error) { 22 | throw new Error(error.message) 23 | } 24 | } -------------------------------------------------------------------------------- /sam-or-cdk/sam/lambda/uploadSigner/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hello_world", 3 | "version": "1.0.0", 4 | "description": "hello world sample for NodeJS", 5 | "main": "app.js", 6 | "repository": "https://github.com/awslabs/aws-sam-cli/tree/develop/samcli/local/init/templates/cookiecutter-aws-sam-hello-nodejs", 7 | "author": "SAM CLI", 8 | "license": "MIT", 9 | "dependencies": {}, 10 | "scripts": { 11 | "test": "mocha tests/unit/" 12 | }, 13 | "devDependencies": { 14 | "chai": "^4.2.0", 15 | "mocha": "^8.2.1" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /sam-or-cdk/sam/lambda/uploadSigner/tests/unit/test-handler.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const app = require('../../app.js'); 4 | const chai = require('chai'); 5 | const expect = chai.expect; 6 | var event, context; 7 | 8 | describe('Tests index', function () { 9 | it('verifies successful response', async () => { 10 | const result = await app.lambdaHandler(event, context) 11 | 12 | expect(result).to.be.an('object'); 13 | expect(result.statusCode).to.equal(200); 14 | expect(result.body).to.be.an('string'); 15 | 16 | let response = JSON.parse(result.body); 17 | 18 | expect(response).to.be.an('object'); 19 | expect(response.message).to.be.equal("hello world"); 20 | // expect(response.location).to.be.an("string"); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /secrets/README.md: -------------------------------------------------------------------------------- 1 | 16 | 17 | # AWS Systems Manager Parameter Store & AWS Secrets Manager 18 | 19 | There are two templates in this project. 20 | 21 | 1. Under the create-secrets folder, this template creates SSM and Secrets Manager parameters. 22 | 2. Under the app folder, this template creates a serverless application that utilizes the secrets in different ways. 23 | -------------------------------------------------------------------------------- /secrets/app/src/app.js: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | // // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this 5 | // software and associated documentation files (the "Software"), to deal in the Software 6 | // without restriction, including without limitation the rights to use, copy, modify, 7 | // merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 8 | // permit persons to whom the Software is furnished to do so. 9 | // // 10 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 11 | // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 12 | // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 13 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 14 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 15 | // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 16 | 17 | exports.lambdaHandler = async (event) => JSON.stringify(event); 18 | -------------------------------------------------------------------------------- /secrets/app/template.yaml: -------------------------------------------------------------------------------- 1 | AWSTemplateFormatVersion: '2010-09-09' 2 | Transform: AWS::Serverless-2016-10-31 3 | Description: SSM Parameters 4 | 5 | # Option one ssm dynamic references https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/dynamic-references.html 6 | # Option two secrets manager 7 | Parameters: 8 | DbEngine: 9 | Type: AWS::SSM::Parameter::Value 10 | Default: /myApp/DbEngine 11 | 12 | Resources: 13 | LambdaFunction: 14 | Type: AWS::Serverless::Function 15 | Properties: 16 | CodeUri: src/ 17 | Runtime: nodejs16.x 18 | Handler: app.lambdaHandler 19 | Environment: 20 | Variables: 21 | DB_ENGINE: !Ref DbEngine 22 | DB_VERSION: '{{resolve:ssm:/myApp/DbVersion:1}}' # SSM requires version 23 | DB_NAME: '{{resolve:secretsmanager:/myApp/DbName}}' 24 | DB_USERNAME: '{{resolve:secretsmanager:/myApp/DbCreds:SecretString:Username}}' 25 | DB_PASSWORD: '{{resolve:secretsmanager:/myApp/DbCreds:SecretString:Password}}' -------------------------------------------------------------------------------- /secrets/create-secrets/template.yaml: -------------------------------------------------------------------------------- 1 | AWSTemplateFormatVersion: '2010-09-09' 2 | Transform: AWS::Serverless-2016-10-31 3 | Description: Secrets for my application 4 | 5 | Parameters: 6 | DbEngine: 7 | Type: String 8 | Description: DB Engine 9 | Default: MySQL 10 | 11 | DbVersion: 12 | Type: String 13 | Description: DB Version 14 | 15 | DbName: 16 | Type: String 17 | Description: Name of DB to use 18 | NoEcho: true 19 | 20 | DbUsername: 21 | Type: String 22 | Description: Username for DB 23 | NoEcho: true 24 | 25 | DbPassword: 26 | Type: String 27 | Description: Password for DB 28 | NoEcho: true 29 | 30 | Resources: 31 | DbEngineParameter: 32 | Type: AWS::SSM::Parameter 33 | Properties: 34 | Name: /myApp/DbEngine 35 | Type: String 36 | Value: !Ref DbEngine 37 | 38 | DbVersionParameter: 39 | Type: AWS::SSM::Parameter 40 | Properties: 41 | Name: /myApp/DbVersion 42 | Type: String 43 | Value: !Ref DbVersion 44 | 45 | DbNameParameter: 46 | Type: AWS::SecretsManager::Secret 47 | Properties: 48 | Name: /myApp/DbName 49 | SecretString: !Ref DbName 50 | 51 | DbCredsParameter: 52 | Type: AWS::SecretsManager::Secret 53 | Properties: 54 | Name: /myApp/DbCreds 55 | SecretString: !Sub "{\"Username\":\"${DbUsername}\",\"Password\":\"${DbPassword}\"}" 56 | -------------------------------------------------------------------------------- /starter-templates/web-app.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/sessions-with-aws-sam/78ebaa71baadf092e13e635c1a0e89e7c49f802f/starter-templates/web-app.zip -------------------------------------------------------------------------------- /starter-templates/web-app/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | samconfig.toml 3 | -------------------------------------------------------------------------------- /starter-templates/web-app/.npmignore: -------------------------------------------------------------------------------- 1 | __tests__/* 2 | events/* 3 | buildspec.yaml 4 | template.yaml 5 | env.json -------------------------------------------------------------------------------- /starter-templates/web-app/__tests__/unit/get-all-items.test.js: -------------------------------------------------------------------------------- 1 | // Import all functions from get-all-items.js 2 | const lambda = require('../../src/get-all-items.js'); 3 | // Import dynamodb from aws-sdk 4 | const dynamodb = require('aws-sdk/clients/dynamodb'); 5 | 6 | // This includes all tests for getAllItemsHandler() 7 | describe('Test getAllItemsHandler', () => { 8 | let scanSpy; 9 | 10 | // Test one-time setup and teardown, see more in https://jestjs.io/docs/en/setup-teardown 11 | beforeAll(() => { 12 | // Mock dynamodb get and put methods 13 | // https://jestjs.io/docs/en/jest-object.html#jestspyonobject-methodname 14 | scanSpy = jest.spyOn(dynamodb.DocumentClient.prototype, 'scan'); 15 | }); 16 | 17 | // Clean up mocks 18 | afterAll(() => { 19 | scanSpy.mockRestore(); 20 | }); 21 | 22 | it('should return ids', async () => { 23 | const items = [{ id: 'id1' }, { id: 'id2' }]; 24 | 25 | // Return the specified value whenever the spied scan function is called 26 | scanSpy.mockReturnValue({ 27 | promise: () => Promise.resolve({ Items: items }) 28 | }); 29 | 30 | const event = { 31 | httpMethod: 'GET' 32 | } 33 | 34 | // Invoke helloFromLambdaHandler() 35 | const result = await lambda.getAllItemsHandler(event); 36 | 37 | const expectedResult = items; 38 | 39 | // Compare the result with the expected result 40 | expect(result).toEqual(expectedResult); 41 | }); 42 | }); 43 | -------------------------------------------------------------------------------- /starter-templates/web-app/__tests__/unit/get-by-id.test.js: -------------------------------------------------------------------------------- 1 | // Import all functions from get-by-id.js 2 | const lambda = require('../../src/get-by-id.js'); 3 | // Import dynamodb from aws-sdk 4 | const dynamodb = require('aws-sdk/clients/dynamodb'); 5 | 6 | // This includes all tests for getByIdHandler() 7 | describe('Test getByIdHandler', () => { 8 | let getSpy; 9 | 10 | // Test one-time setup and teardown, see more in https://jestjs.io/docs/en/setup-teardown 11 | beforeAll(() => { 12 | // Mock dynamodb get and put methods 13 | // https://jestjs.io/docs/en/jest-object.html#jestspyonobject-methodname 14 | getSpy = jest.spyOn(dynamodb.DocumentClient.prototype, 'get'); 15 | }); 16 | 17 | // Clean up mocks 18 | afterAll(() => { 19 | getSpy.mockRestore(); 20 | }); 21 | 22 | // This test invokes getByIdHandler() and compare the result 23 | it('should get item by id', async () => { 24 | const item = { id: 'id1' }; 25 | 26 | // Return the specified value whenever the spied get function is called 27 | getSpy.mockReturnValue({ 28 | promise: () => Promise.resolve({ Item: item }) 29 | }); 30 | 31 | const event = { 32 | httpMethod: 'GET', 33 | pathParameters: { 34 | id: 'id1' 35 | } 36 | } 37 | 38 | // Invoke getByIdHandler() 39 | const result = await lambda.getByIdHandler(event); 40 | 41 | const expectedResult = item; 42 | 43 | // Compare the result with the expected result 44 | expect(result).toEqual(expectedResult); 45 | }); 46 | }); 47 | -------------------------------------------------------------------------------- /starter-templates/web-app/__tests__/unit/put-item.test.js: -------------------------------------------------------------------------------- 1 | // Import all functions from put-item.js 2 | const lambda = require('../../src/put-item.js'); 3 | // Import dynamodb from aws-sdk 4 | const dynamodb = require('aws-sdk/clients/dynamodb'); 5 | 6 | // This includes all tests for putItemHandler() 7 | describe('Test putItemHandler', function () { 8 | let putSpy; 9 | 10 | // Test one-time setup and teardown, see more in https://jestjs.io/docs/en/setup-teardown 11 | beforeAll(() => { 12 | // Mock dynamodb get and put methods 13 | // https://jestjs.io/docs/en/jest-object.html#jestspyonobject-methodname 14 | putSpy = jest.spyOn(dynamodb.DocumentClient.prototype, 'put'); 15 | }); 16 | 17 | // Clean up mocks 18 | afterAll(() => { 19 | putSpy.mockRestore(); 20 | }); 21 | 22 | // This test invokes putItemHandler() and compare the result 23 | it('should add id to the table', async () => { 24 | const returnedItem = { id: 'id1', name: 'name1' }; 25 | 26 | // Return the specified value whenever the spied put function is called 27 | putSpy.mockReturnValue({ 28 | promise: () => Promise.resolve(returnedItem) 29 | }); 30 | 31 | const event = { 32 | httpMethod: 'POST', 33 | body: '{"id": "id1","name": "name1"}' 34 | }; 35 | 36 | // Invoke putItemHandler() 37 | const result = await lambda.putItemHandler(event); 38 | const expectedResult = returnedItem; 39 | 40 | // Compare the result with the expected result 41 | expect(result).toEqual(expectedResult); 42 | }); 43 | }); 44 | -------------------------------------------------------------------------------- /starter-templates/web-app/buildspec.yaml: -------------------------------------------------------------------------------- 1 | version: 0.2 2 | phases: 3 | install: 4 | commands: 5 | # Install all dependencies (including dependencies for running tests) 6 | - npm install 7 | pre_build: 8 | commands: 9 | # Discover and run unit tests in the '__tests__' directory 10 | - npm run test 11 | # Remove all unit tests to reduce the size of the package that will be ultimately uploaded to Lambda 12 | - rm -rf ./__tests__ 13 | # Remove all dependencies not needed for the Lambda deployment package (the packages from devDependencies in package.json) 14 | - npm prune --production 15 | build: 16 | commands: 17 | # Use AWS SAM to package the application by using AWS CloudFormation 18 | - aws cloudformation package --template template.yml --s3-bucket $S3_BUCKET --output-template template-export.yml 19 | artifacts: 20 | type: zip 21 | files: 22 | - template-export.yml 23 | -------------------------------------------------------------------------------- /starter-templates/web-app/env.json: -------------------------------------------------------------------------------- 1 | { 2 | "getAllItemsFunction": { 3 | "SAMPLE_TABLE": "" 4 | }, 5 | "getByIdFunction": { 6 | "SAMPLE_TABLE": "" 7 | }, 8 | "putItemFunction": { 9 | "SAMPLE_TABLE": "" 10 | } 11 | } -------------------------------------------------------------------------------- /starter-templates/web-app/events/event-get-all-items.json: -------------------------------------------------------------------------------- 1 | { 2 | "httpMethod": "GET" 3 | } -------------------------------------------------------------------------------- /starter-templates/web-app/events/event-get-by-id.json: -------------------------------------------------------------------------------- 1 | { 2 | "httpMethod": "GET", 3 | "pathParameters": { 4 | "id": "id1" 5 | } 6 | } -------------------------------------------------------------------------------- /starter-templates/web-app/events/event-post-item.json: -------------------------------------------------------------------------------- 1 | { 2 | "body": "{\"id\": \"id1\",\"name\": \"name1\"}" 3 | } 4 | -------------------------------------------------------------------------------- /starter-templates/web-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "delete-test-01", 3 | "description": "delete-test-01-description", 4 | "version": "0.0.1", 5 | "private": true, 6 | "dependencies": { 7 | "aws-sdk": "^2.904.0" 8 | }, 9 | "devDependencies": { 10 | "jest": "^26.6.3" 11 | }, 12 | "scripts": { 13 | "test": "jest" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /starter-templates/web-app/src/get-all-items.js: -------------------------------------------------------------------------------- 1 | const tableName = process.env.SAMPLE_TABLE; 2 | const dynamodb = require('aws-sdk/clients/dynamodb'); 3 | const docClient = new dynamodb.DocumentClient(); 4 | 5 | exports.getAllItemsHandler = async (event) => { 6 | var params = { 7 | TableName : tableName 8 | }; 9 | const data = await docClient.scan(params).promise(); 10 | return data.Items; 11 | } -------------------------------------------------------------------------------- /starter-templates/web-app/src/get-by-id.js: -------------------------------------------------------------------------------- 1 | const tableName = process.env.SAMPLE_TABLE; 2 | const dynamodb = require('aws-sdk/clients/dynamodb'); 3 | const docClient = new dynamodb.DocumentClient(); 4 | 5 | exports.getByIdHandler = async (event) => { 6 | const id = event.pathParameters.id; 7 | 8 | var params = { 9 | TableName : tableName, 10 | Key: { id: id }, 11 | }; 12 | const data = await docClient.get(params).promise(); 13 | return data.Item; 14 | } 15 | -------------------------------------------------------------------------------- /starter-templates/web-app/src/put-item.js: -------------------------------------------------------------------------------- 1 | const dynamodb = require('aws-sdk/clients/dynamodb'); 2 | const docClient = new dynamodb.DocumentClient(); 3 | const tableName = process.env.SAMPLE_TABLE; 4 | 5 | exports.putItemHandler = async (event) => { 6 | const body = JSON.parse(event.body) 7 | const id = body.id; 8 | const name = body.name; 9 | 10 | var params = { 11 | TableName : tableName, 12 | Item: { id : id, name: name } 13 | }; 14 | 15 | const result = await docClient.put(params).promise(); 16 | return body 17 | } 18 | -------------------------------------------------------------------------------- /starter-templates/web-app/template.yaml: -------------------------------------------------------------------------------- 1 | AWSTemplateFormatVersion: 2010-09-09 2 | Description: Web app 3 | Transform: AWS::Serverless-2016-10-31 4 | 5 | Globals: 6 | Function: 7 | CodeUri: . 8 | Runtime: nodejs16.x 9 | MemorySize: 128 10 | Timeout: 100 11 | Environment: 12 | Variables: 13 | SAMPLE_TABLE: !Ref SampleTable 14 | 15 | Resources: 16 | getAllItemsFunction: 17 | Type: AWS::Serverless::Function 18 | Properties: 19 | Handler: src/get-all-items.getAllItemsHandler 20 | Description: get all items 21 | Policies: 22 | - DynamoDBReadPolicy: 23 | TableName: !Ref SampleTable 24 | Events: 25 | Api: 26 | Type: HttpApi 27 | Properties: 28 | Path: / 29 | Method: GET 30 | 31 | getByIdFunction: 32 | Type: AWS::Serverless::Function 33 | Properties: 34 | Handler: src/get-by-id.getByIdHandler 35 | Description: get item by id 36 | Policies: 37 | - DynamoDBReadPolicy: 38 | TableName: !Ref SampleTable 39 | Events: 40 | Api: 41 | Type: HttpApi 42 | Properties: 43 | Path: /{id} 44 | Method: GET 45 | 46 | putItemFunction: 47 | Type: AWS::Serverless::Function 48 | Properties: 49 | Handler: src/put-item.putItemHandler 50 | Description: put item in ddb table 51 | Policies: 52 | - DynamoDBCrudPolicy: 53 | TableName: !Ref SampleTable 54 | Events: 55 | Api: 56 | Type: HttpApi 57 | Properties: 58 | Path: / 59 | Method: POST 60 | 61 | SampleTable: 62 | Type: AWS::Serverless::SimpleTable 63 | 64 | Outputs: 65 | WebEndpoint: 66 | Description: "API Gateway endpoint URL for Prod stage" 67 | Value: !Sub "https://${ServerlessHttpApi}.execute-api.${AWS::Region}.amazonaws.com" 68 | -------------------------------------------------------------------------------- /step-functions/.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/api/osx,node,linux,windows 3 | 4 | ### Linux ### 5 | *~ 6 | 7 | # temporary files which can be created if a process still has a handle open of a deleted file 8 | .fuse_hidden* 9 | 10 | # KDE directory preferences 11 | .directory 12 | 13 | # Linux trash folder which might appear on any partition or disk 14 | .Trash-* 15 | 16 | # .nfs files are created when an open file is removed but is still being accessed 17 | .nfs* 18 | 19 | ### Node ### 20 | # Logs 21 | logs 22 | *.log 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | 27 | # Runtime data 28 | pids 29 | *.pid 30 | *.seed 31 | *.pid.lock 32 | 33 | # Directory for instrumented libs generated by jscoverage/JSCover 34 | lib-cov 35 | 36 | # Coverage directory used by tools like istanbul 37 | coverage 38 | 39 | # nyc test coverage 40 | .nyc_output 41 | 42 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 43 | .grunt 44 | 45 | # Bower dependency directory (https://bower.io/) 46 | bower_components 47 | 48 | # node-waf configuration 49 | .lock-wscript 50 | 51 | # Compiled binary addons (http://nodejs.org/api/addons.html) 52 | build/Release 53 | 54 | # Dependency directories 55 | node_modules/ 56 | jspm_packages/ 57 | 58 | # Typescript v1 declaration files 59 | typings/ 60 | 61 | # Optional npm cache directory 62 | .npm 63 | 64 | # Optional eslint cache 65 | .eslintcache 66 | 67 | # Optional REPL history 68 | .node_repl_history 69 | 70 | # Output of 'npm pack' 71 | *.tgz 72 | 73 | # Yarn Integrity file 74 | .yarn-integrity 75 | 76 | # dotenv environment variables file 77 | .env 78 | 79 | 80 | ### OSX ### 81 | *.DS_Store 82 | .AppleDouble 83 | .LSOverride 84 | 85 | # Icon must end with two \r 86 | Icon 87 | 88 | # Thumbnails 89 | ._* 90 | 91 | # Files that might appear in the root of a volume 92 | .DocumentRevisions-V100 93 | .fseventsd 94 | .Spotlight-V100 95 | .TemporaryItems 96 | .Trashes 97 | .VolumeIcon.icns 98 | .com.apple.timemachine.donotpresent 99 | 100 | # Directories potentially created on remote AFP share 101 | .AppleDB 102 | .AppleDesktop 103 | Network Trash Folder 104 | Temporary Items 105 | .apdisk 106 | 107 | ### Windows ### 108 | # Windows thumbnail cache files 109 | Thumbs.db 110 | ehthumbs.db 111 | ehthumbs_vista.db 112 | 113 | # Folder config file 114 | Desktop.ini 115 | 116 | # Recycle Bin used on file shares 117 | $RECYCLE.BIN/ 118 | 119 | # Windows Installer files 120 | *.cab 121 | *.msi 122 | *.msm 123 | *.msp 124 | 125 | # Windows shortcuts 126 | *.lnk 127 | 128 | 129 | # End of https://www.gitignore.io/api/osx,node,linux,windows -------------------------------------------------------------------------------- /step-functions/README.md: -------------------------------------------------------------------------------- 1 | 16 | 17 | # AWS Step Functions 18 | 19 | In this session, viewers learn how to use AWS SAM to create and deploy AWS Step Functions workflows in their serverless applications. A basic overview of Step Functions is provided. Standard Workflows and Express Workflows are introduced and compared. Nested workflows are introduced for composing complex state machines from basic building blocks. A sample state machine is built using AWS SAM and the AWS Toolkit for Visual Studio Code. Finally, we review what the viewer has learned and conclude the livestream. 20 | -------------------------------------------------------------------------------- /step-functions/statemachine/analytics.asl.json: -------------------------------------------------------------------------------- 1 | { 2 | "Comment": "A state machine that gathers devrel analytics.", 3 | "StartAt": "Launch nested workflows", 4 | "States": { 5 | "Launch nested workflows": { 6 | "Type": "Parallel", 7 | "Branches": [ 8 | { 9 | "StartAt": "Gather Stack Overflow analytics", 10 | "States": { 11 | "Gather Stack Overflow analytics": { 12 | "Type": "Task", 13 | "Resource": "arn:aws:states:::states:startExecution.sync:2", 14 | "Parameters": { 15 | "StateMachineArn": "${StackOverflowWorkflowArn}" 16 | }, 17 | "End": true 18 | } 19 | } 20 | }, 21 | { 22 | "StartAt": "Gather Twitch analytics", 23 | "States": { 24 | "Gather Twitch analytics": { 25 | "Type": "Task", 26 | "Resource": "arn:aws:states:::states:startExecution.sync:2", 27 | "Parameters": { 28 | "StateMachineArn": "${TwitchWorkflowArn}" 29 | }, 30 | "End": true 31 | } 32 | } 33 | } 34 | ], 35 | "Next": "Send update notification" 36 | }, 37 | "Send update notification": { 38 | "Type": "Task", 39 | "Resource": "arn:aws:states:::sns:publish", 40 | "Parameters": { 41 | "TopicArn": "${SNSTopicArn}", 42 | "Message": { 43 | "Input": "Successfully updated analytics!" 44 | } 45 | }, 46 | "End": true 47 | } 48 | } 49 | } -------------------------------------------------------------------------------- /step-functions/statemachine/stackoverflow.asl.json: -------------------------------------------------------------------------------- 1 | { 2 | "Comment": "A Hello World example demonstrating various state types of the Amazon States Language", 3 | "StartAt": "Pass", 4 | "States": { 5 | "Pass": { 6 | "Comment": "A Pass state passes its input to its output, without performing work. Pass states are useful when constructing and debugging state machines.", 7 | "Type": "Pass", 8 | "Next": "Store Analytics" 9 | }, 10 | "Store Analytics": { 11 | "Type": "Task", 12 | "Resource": "${DDBPutItem}", 13 | "Parameters": { 14 | "TableName": "${DDBTable}", 15 | "Item": { 16 | "Id": { 17 | "S.$": "$$.Execution.Id" 18 | }, 19 | "Platform": { 20 | "S": "Stack Overflow" 21 | }, 22 | "Timestamp": { 23 | "S.$": "$$.State.EnteredTime" 24 | } 25 | } 26 | }, 27 | "End": true 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /step-functions/statemachine/twitch.asl.json: -------------------------------------------------------------------------------- 1 | { 2 | "Comment": "A Hello World example demonstrating various state types of the Amazon States Language", 3 | "StartAt": "Pass", 4 | "States": { 5 | "Pass": { 6 | "Comment": "A Pass state passes its input to its output, without performing work. Pass states are useful when constructing and debugging state machines.", 7 | "Type": "Pass", 8 | "Next": "Store Analytics" 9 | }, 10 | "Store Analytics": { 11 | "Type": "Task", 12 | "Resource": "${DDBPutItem}", 13 | "Parameters": { 14 | "TableName": "${DDBTable}", 15 | "Item": { 16 | "Id": { 17 | "S.$": "$$.Execution.Id" 18 | }, 19 | "Platform": { 20 | "S": "Twitch" 21 | }, 22 | "Timestamp": { 23 | "S.$": "$$.State.EnteredTime" 24 | } 25 | } 26 | }, 27 | "End": true 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /swift-custom-runtime/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /Packages 3 | /*.xcodeproj 4 | xcuserdata/ 5 | 6 | .aws-sam 7 | .build 8 | *.resolved -------------------------------------------------------------------------------- /swift-custom-runtime/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM swift:5.2-amazonlinux2 2 | 3 | RUN yum -y install zip openssl-devel 4 | -------------------------------------------------------------------------------- /swift-custom-runtime/Makefile: -------------------------------------------------------------------------------- 1 | ### Add functions here and link them to builder-bit format MUST BE "build-FunctionResourceName in template.yaml" 2 | 3 | build-Squared: builder-bot 4 | build-SwiftApi: builder-bot 5 | 6 | ###################### No Change required below this line ########################## 7 | 8 | builder-bot: 9 | $(eval $@PRODUCT = $(subst build-,,$(MAKECMDGOALS))) 10 | $(eval $@BUILD_DIR = $(PWD)/.aws-sam/build-$($@PRODUCT)) 11 | $(eval $@STAGE = $($@BUILD_DIR)/lambda) 12 | $(eval $@ARTIFACTS_DIR = $(PWD)/.aws-sam/build/$($@PRODUCT)) 13 | 14 | # Create docker file 15 | # docker build -f Dockerfile . -t builder 16 | 17 | # prep directories 18 | mkdir -p $($@BUILD_DIR)/lambda $($@ARTIFACTS_DIR) 19 | 20 | # Compile application 21 | docker run --rm -v $($@BUILD_DIR):/build-target -v `pwd`:/build-src -w /build-src builder bash -cl "swift build --product $($@PRODUCT) -c release --build-path /build-target" 22 | 23 | # copy deps 24 | -docker run --rm -v $($@BUILD_DIR):/build-target -v `pwd`:/build-src -w /build-src builder bash -cl "ldd '/build-target/release/$($@PRODUCT)' | grep swift | awk '{print $3}' | xargs cp -Lv -t /build-target/lambda" 25 | 26 | # copy binary to stage 27 | cp $($@BUILD_DIR)/release/$($@PRODUCT) $($@BUILD_DIR)/lambda/bootstrap 28 | 29 | # copy app from stage to artifacts dir 30 | cp $($@STAGE)/* $($@ARTIFACTS_DIR) -------------------------------------------------------------------------------- /swift-custom-runtime/Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.2 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | import PackageDescription 5 | 6 | let package = Package( 7 | name: "SwiftApp", 8 | platforms: [ 9 | .macOS(.v10_13), 10 | ], 11 | products: [ 12 | .executable(name: "Squared", targets: ["Squared"]), 13 | .executable(name: "SwiftApi", targets: ["SwiftApi"]) 14 | ], 15 | dependencies: [ 16 | .package(url: "https://github.com/swift-server/swift-aws-lambda-runtime.git", .upToNextMajor(from:"0.2.0")), 17 | ], 18 | targets: [ 19 | .target( 20 | name: "Squared", 21 | dependencies: [ 22 | .product(name: "AWSLambdaRuntime", package: "swift-aws-lambda-runtime"), 23 | ] 24 | ), 25 | .target( 26 | name: "SwiftApi", 27 | dependencies: [ 28 | .product(name: "AWSLambdaRuntime", package: "swift-aws-lambda-runtime"), 29 | .product(name: "AWSLambdaEvents", package: "swift-aws-lambda-runtime"), 30 | ] 31 | ), 32 | ] 33 | ) 34 | -------------------------------------------------------------------------------- /swift-custom-runtime/Sources/Squared/main.swift: -------------------------------------------------------------------------------- 1 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this 2 | // software and associated documentation files (the "Software"), to deal in the Software 3 | // without restriction, including without limitation the rights to use, copy, modify, 4 | // merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 5 | // permit persons to whom the Software is furnished to do so. 6 | // // 7 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 8 | // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 9 | // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 10 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 11 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 12 | // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 13 | 14 | import AWSLambdaRuntime 15 | 16 | struct Input: Codable { 17 | let number: Double 18 | } 19 | 20 | struct Output: Codable { 21 | let result: Double 22 | } 23 | 24 | Lambda.run { (context, input: Input, callback: @escaping (Result) -> Void) in 25 | callback(.success(Output(result: input.number * input.number))) 26 | } -------------------------------------------------------------------------------- /swift-custom-runtime/Sources/SwiftApi/main.swift: -------------------------------------------------------------------------------- 1 | import AWSLambdaEvents 2 | import AWSLambdaRuntime 3 | import NIO 4 | 5 | // MARK: - Run Lambda 6 | Lambda.run(APIGatewayProxyLambda()) 7 | 8 | // MARK: - Handler, Request and Response 9 | // FIXME: Use proper Event abstractions once added to AWSLambdaRuntime 10 | struct APIGatewayProxyLambda: EventLoopLambdaHandler { 11 | public typealias In = APIGateway.Request 12 | public typealias Out = APIGateway.Response 13 | 14 | public func handle(context: Lambda.Context, event: APIGateway.Request) -> EventLoopFuture { 15 | context.logger.debug("hello, api gateway! How are you?") 16 | return context.eventLoop.makeSucceededFuture(APIGateway.Response(statusCode: .ok, body: "Hello, api gateway! How are you today?")) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /swift-custom-runtime/events/squared-event.json: -------------------------------------------------------------------------------- 1 | { 2 | "number": 9 3 | } -------------------------------------------------------------------------------- /swift-custom-runtime/template.yaml: -------------------------------------------------------------------------------- 1 | AWSTemplateFormatVersion: '2010-09-09' 2 | Transform: AWS::Serverless-2016-10-31 3 | Description: Swift Application 4 | 5 | Globals: 6 | Function: 7 | Timeout: 10 8 | CodeUri: . 9 | Handler: swift.bootstrap 10 | Runtime: provided.al2 11 | MemorySize: 512 12 | 13 | Resources: 14 | SwiftApi: 15 | Type: AWS::Serverless::Function 16 | Properties: 17 | Events: 18 | ApiTrigger: 19 | Type: Api 20 | Properties: 21 | Path: / 22 | Method: get 23 | Metadata: 24 | BuildMethod: makefile 25 | 26 | Squared: 27 | Type: AWS::Serverless::Function 28 | Properties: {} 29 | Metadata: 30 | BuildMethod: makefile 31 | 32 | Outputs: 33 | ApiEndpoint: 34 | Description: "API endpoint URL" 35 | Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod" --------------------------------------------------------------------------------