diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index b50fa2907..31562ac68 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -10,7 +10,10 @@ - [ ] `make pr` passes - [ ] Update documentation - [ ] Verify transformed template deploys and application functions as expected -- [ ] Add/update example to `examples/2016-10-31` +*Examples?* + +Please reach out in the comments, if you want to add an example. Examples will be +added to `sam init` through https://github.com/awslabs/aws-sam-cli-app-templates/ By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license. diff --git a/docs/cloudformation_compatibility.rst b/docs/cloudformation_compatibility.rst index ef104fad7..50b973612 100644 --- a/docs/cloudformation_compatibility.rst +++ b/docs/cloudformation_compatibility.rst @@ -108,6 +108,16 @@ StartingPosition All BatchSize All ======================== ================================== ======================== +MSK +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +======================== ================================== ======================== + Property Name Intrinsic(s) Supported Reasons +======================== ================================== ======================== +Stream All +Topics All +StartingPosition All +======================== ================================== ======================== + DynamoDB ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ======================== ================================== ======================== diff --git a/docs/internals/generated_resources.rst b/docs/internals/generated_resources.rst index 4c438b94d..760d52a15 100644 --- a/docs/internals/generated_resources.rst +++ b/docs/internals/generated_resources.rst @@ -326,6 +326,34 @@ AWS::Lambda::Permission MyFunction\ **MyTrigger**\ Permission AWS::Lambda::EventSourceMapping MyFunction\ **MyTrigger** ================================== ================================ +MSK +^^^^^^^ + +Example: + +.. code:: yaml + + MyFunction: + Type: AWS::Serverless::Function + Properties: + ... + Events: + MyTrigger: + Type: MSK + Properties: + Stream: arn:aws:kafka:us-east-1:123456789012:cluster/mycluster/6cc0432b-8618-4f44-bccc-e1fbd8fb7c4d-2 + StartingPosition: TRIM_HORIZON + ... + +Additional generated resources: + +================================== ================================ +CloudFormation Resource Type Logical ID +================================== ================================ +AWS::Lambda::Permission MyFunction\ **MyTrigger**\ Permission +AWS::Lambda::EventSourceMapping MyFunction\ **MyTrigger** +================================== ================================ + SQS ^^^ diff --git a/examples/.eslintrc.yml b/examples/.eslintrc.yml deleted file mode 100644 index f13cc3238..000000000 --- a/examples/.eslintrc.yml +++ /dev/null @@ -1,3 +0,0 @@ -extends: standard -rules: - prefer-promise-reject-errors: off # API Gateway expects string response from Lamdba (when using async + Promise.reject) diff --git a/examples/2016-10-31/alexa_skill/index.js b/examples/2016-10-31/alexa_skill/index.js deleted file mode 100644 index 2b0de8b13..000000000 --- a/examples/2016-10-31/alexa_skill/index.js +++ /dev/null @@ -1,33 +0,0 @@ -'use strict'; -console.log('Loading hello world function'); - -exports.handler = function(event, context) { - var name = "World"; - var responseCode = 200; - console.log("request: " + JSON.stringify(event)); - if (event.queryStringParameters !== null && event.queryStringParameters !== undefined) { - if (event.queryStringParameters.name !== undefined && event.queryStringParameters.name !== null && event.queryStringParameters.name !== "") { - console.log("Received name: " + event.queryStringParameters.name); - name = event.queryStringParameters.name; - } - - if (event.queryStringParameters.httpStatus !== undefined && event.queryStringParameters.httpStatus !== null && event.queryStringParameters.httpStatus !== "") { - console.log("Received http status: " + event.queryStringParameters.httpStatus); - responseCode = event.queryStringParameters.httpStatus; - } - } - - var responseBody = { - message: "Hello " + name + "!", - input: event - }; - var response = { - statusCode: responseCode, - headers: { - "x-custom-header" : "my custom header value" - }, - body: JSON.stringify(responseBody) - }; - console.log("response: " + JSON.stringify(response)) - context.succeed(response); -}; diff --git a/examples/2016-10-31/alexa_skill/template.yaml b/examples/2016-10-31/alexa_skill/template.yaml deleted file mode 100644 index 6177d4da8..000000000 --- a/examples/2016-10-31/alexa_skill/template.yaml +++ /dev/null @@ -1,13 +0,0 @@ -AWSTemplateFormatVersion: '2010-09-09' -Transform: AWS::Serverless-2016-10-31 -Description: Alexa Skill https://developer.amazon.com/alexa-skills-kit -Resources: - AlexaSkillFunction: - Type: AWS::Serverless::Function - Properties: - CodeUri: s3:///alexa_skill.zip - Handler: index.handler - Runtime: nodejs12.x - Events: - AlexaSkillEvent: - Type: AlexaSkill diff --git a/examples/2016-10-31/api_aws_iam_auth/index.js b/examples/2016-10-31/api_aws_iam_auth/index.js deleted file mode 100644 index 472369c6c..000000000 --- a/examples/2016-10-31/api_aws_iam_auth/index.js +++ /dev/null @@ -1,7 +0,0 @@ -exports.handler = async (event) => { - return { - statusCode: 200, - body: JSON.stringify(event), - headers: {} - } -} diff --git a/examples/2016-10-31/api_aws_iam_auth/template.yaml b/examples/2016-10-31/api_aws_iam_auth/template.yaml deleted file mode 100644 index a3ad3369a..000000000 --- a/examples/2016-10-31/api_aws_iam_auth/template.yaml +++ /dev/null @@ -1,30 +0,0 @@ -AWSTemplateFormatVersion: '2010-09-09' -Transform: AWS::Serverless-2016-10-31 -Description: API Gateway with AWS IAM Authorizer -Resources: - MyApi: - Type: AWS::Serverless::Api - Properties: - StageName: Prod - Auth: - DefaultAuthorizer: AWS_IAM - InvokeRole: CALLER_CREDENTIALS # default, can specify other role or NONE - - MyFunction: - Type: AWS::Serverless::Function - Properties: - CodeUri: . - Handler: index.handler - Runtime: nodejs12.x - Events: - GetRoot: - Type: Api - Properties: - RestApiId: !Ref MyApi - Path: / - Method: get - -Outputs: - ApiURL: - Description: "API URL" - Value: !Sub 'https://${MyApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/' diff --git a/examples/2016-10-31/api_backend/src/index.js b/examples/2016-10-31/api_backend/src/index.js deleted file mode 100644 index e53321cf1..000000000 --- a/examples/2016-10-31/api_backend/src/index.js +++ /dev/null @@ -1,81 +0,0 @@ -'use strict'; - - -const AWS = require('aws-sdk'); -const dynamo = new AWS.DynamoDB.DocumentClient(); - -const tableName = process.env.TABLE_NAME; - -const createResponse = (statusCode, body) => ({ statusCode, body }); - -exports.get = (event, context, callback) => { - - let params = { - TableName: tableName, - Key: { - id: event.pathParameters.resourceId - } - }; - - let dbGet = (params) => { return dynamo.get(params).promise() }; - - dbGet(params).then( (data) => { - if (!data.Item) { - callback(null, createResponse(404, "ITEM NOT FOUND")); - return; - } - console.log(`RETRIEVED ITEM SUCCESSFULLY WITH doc = ${data.Item.doc}`); - callback(null, createResponse(200, data.Item.doc)); - }).catch( (err) => { - console.log(`GET ITEM FAILED FOR doc = ${params.Key.id}, WITH ERROR: ${err}`); - callback(null, createResponse(500, err)); - }); -}; - -exports.put = (event, context, callback) => { - - let item = { - id: event.pathParameters.resourceId, - doc: event.body - }; - - let params = { - TableName: tableName, - Item: item - }; - - let dbPut = (params) => { return dynamo.put(params).promise() }; - - dbPut(params).then( (data) => { - console.log(`PUT ITEM SUCCEEDED WITH doc = ${item.doc}`); - callback(null, createResponse(200, null)); - }).catch( (err) => { - console.log(`PUT ITEM FAILED FOR doc = ${item.doc}, WITH ERROR: ${err}`); - callback(null, createResponse(500, err)); - }); -}; - -exports.delete = (event, context, callback) => { - - let params = { - TableName: tableName, - Key: { - id: event.pathParameters.resourceId - }, - ReturnValues: 'ALL_OLD' - }; - - let dbDelete = (params) => { return dynamo.delete(params).promise() }; - - dbDelete(params).then( (data) => { - if (!data.Attributes) { - callback(null, createResponse(404, "ITEM NOT FOUND FOR DELETION")); - return; - } - console.log(`DELETED ITEM SUCCESSFULLY WITH id = ${event.pathParameters.resourceId}`); - callback(null, createResponse(200, null)); - }).catch( (err) => { - console.log(`DELETE ITEM FAILED FOR id = ${event.pathParameters.resourceId}, WITH ERROR: ${err}`); - callback(null, createResponse(500, err)); - }); -}; diff --git a/examples/2016-10-31/api_backend/template.yaml b/examples/2016-10-31/api_backend/template.yaml deleted file mode 100644 index 196527fca..000000000 --- a/examples/2016-10-31/api_backend/template.yaml +++ /dev/null @@ -1,68 +0,0 @@ -AWSTemplateFormatVersion: "2010-09-09" -Transform: AWS::Serverless-2016-10-31 -Description: Simple CRUD webservice. State is stored in a SimpleTable (DynamoDB) resource. -Resources: - GetFunction: - Type: AWS::Serverless::Function - Properties: - Handler: index.get - Runtime: nodejs12.x - CodeUri: src/ - Policies: - - DynamoDBReadPolicy: - TableName: !Ref Table - Environment: - Variables: - TABLE_NAME: !Ref Table - Events: - GetResource: - Type: Api - Properties: - Path: /resource/{resourceId} - Method: get - - PutFunction: - Type: AWS::Serverless::Function - Properties: - Handler: index.put - Runtime: nodejs12.x - CodeUri: src/ - Policies: - - DynamoDBCrudPolicy: - TableName: !Ref Table - Environment: - Variables: - TABLE_NAME: !Ref Table - Events: - PutResource: - Type: Api - Properties: - Path: /resource/{resourceId} - Method: put - - DeleteFunction: - Type: AWS::Serverless::Function - Properties: - Handler: index.delete - Runtime: nodejs12.x - CodeUri: src/ - Policies: - - DynamoDBCrudPolicy: - TableName: !Ref Table - Environment: - Variables: - TABLE_NAME: !Ref Table - Events: - DeleteResource: - Type: Api - Properties: - Path: /resource/{resourceId} - Method: delete - - Table: - Type: AWS::Serverless::SimpleTable - -Outputs: - ApiURL: - Description: "API endpoint URL for Prod environment" - Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/resource/" diff --git a/examples/2016-10-31/api_cognito_auth/README.md b/examples/2016-10-31/api_cognito_auth/README.md deleted file mode 100644 index 5eae27aa2..000000000 --- a/examples/2016-10-31/api_cognito_auth/README.md +++ /dev/null @@ -1,45 +0,0 @@ -# API Gateway + Cognito Auth + Cognito Hosted Auth Example - -This example shows you how to create an API Gateway API with a Cognito Authorizer using SAM. - -## Running the example - -Install the Node.js/NPM dependencies for your API's Lambda logic into the `src/` directory. This is necessary so that the dependencies get packaged up along with your Lambda function. - -```bash -npm install . --prefix ./src -``` - -Deploy the example into your account (replace `YOUR_S3_ARTIFACTS_BUCKET` with an existing S3 bucket to store your app assets): - -```bash -# The following default values are also allowed: STACK_NAME, COGNITO_USER_POOL_CLIENT_NAME, COGNITO_USER_POOL_DOMAIN_PREFIX -S3_BUCKET_NAME=YOUR_S3_ARTIFACTS_BUCKET \ -npm run package-deploy -``` - -Cognito User Pools doesn't currently have CloudFormation support for configuring their Hosted Register/Signin UI. For now we will create these via the AWS CLI: - -```bash -npm run configure-cognito-user-pool -``` - -Open the registration page created and hosted for you by Cognito in your browser. After the page loads, enter a Username and Password and click the Sign Up button. - -```bash -npm run open-signup-page - -# Alternatively, you can open the login page by running `npm run open-login-page` -``` - -After clicking Sign Up, you will be redirected to the UI client for your API. - -To access the API UI directly as an unauthorized user (who has access to `GET /users` and `GET /users/{userId}`) you can run `npm run open-api-ui`. - -## Additional resources - -- https://aws.amazon.com/blogs/aws/launch-amazon-cognito-user-pools-general-availability-app-integration-and-federation/ -- https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-integrate-with-cognito.html -- https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pools-app-idp-settings.html -- https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-invoke-api-integrated-with-cognito-user-pool.html -- https://docs.aws.amazon.com/cognito/latest/developerguide/token-endpoint.html \ No newline at end of file diff --git a/examples/2016-10-31/api_cognito_auth/package.json b/examples/2016-10-31/api_cognito_auth/package.json deleted file mode 100644 index 6bd1d43b3..000000000 --- a/examples/2016-10-31/api_cognito_auth/package.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "name": "api_cognito_auth", - "version": "1.0.0", - "description": "Example using API Gateway with Cognito Authorizer.", - "main": "lambda.js", - "license": "Apache-2.0", - "dependencies": { - "aws-serverless-express": "^3.3.3", - "body-parser": "^1.17.1", - "cors": "^2.8.3", - "express": "^4.15.2", - "pug": "^2.0.0-rc.1" - }, - "scripts": { - "package-deploy": "npm run set-config && npm run package && npm run deploy", - "set-config": "npm config set STACK_NAME ${STACK_NAME:-sam-example-api-cognito-auth}", - "package": "aws cloudformation package --template-file template.yaml --output-template-file template.packaged.yaml --s3-bucket $S3_BUCKET_NAME", - "deploy": "aws cloudformation deploy --template-file ./template.packaged.yaml --stack-name $STACK_NAME --capabilities CAPABILITY_IAM", - "configure-cognito-user-pool": "npm run set-cognito-user-pool-id && npm run set-cognito-user-pool-client-id && npm run set-api-id && npm run set-api-url && npm run update-user-pool-client && npm run create-user-pool-domain", - "set-cognito-user-pool-id": "npm config set COGNITO_USER_POOL_ID $(aws cloudformation describe-stacks --stack-name $(npm config get STACK_NAME) --query 'Stacks[].Outputs[?OutputKey==`CognitoUserPoolId`].OutputValue' --output text)", - "set-cognito-user-pool-client-id": "npm config set COGNITO_USER_POOL_CLIENT_ID $(aws cloudformation describe-stacks --stack-name $(npm config get STACK_NAME) --query 'Stacks[].Outputs[?OutputKey==`CognitoUserPoolClientId`].OutputValue' --output text)", - "set-api-url": "npm config set API_URL $(aws cloudformation describe-stacks --stack-name $(npm config get STACK_NAME) --query 'Stacks[].Outputs[?OutputKey==`ApiUrl`].OutputValue' --output text)", - "set-api-id": "npm config set API_ID $(aws cloudformation describe-stacks --stack-name $(npm config get STACK_NAME) --query 'Stacks[].Outputs[?OutputKey==`ApiId`].OutputValue' --output text)", - "update-user-pool-client": "aws cognito-idp update-user-pool-client --user-pool-id $(npm config get COGNITO_USER_POOL_ID) --client-id $(npm config get COGNITO_USER_POOL_CLIENT_ID) --supported-identity-providers COGNITO --callback-urls \"[\\\"$(npm config get API_URL)\\\"]\" --allowed-o-auth-flows code implicit --allowed-o-auth-scopes openid email --allowed-o-auth-flows-user-pool-client", - "create-user-pool-domain": "aws cognito-idp create-user-pool-domain --domain $(npm config get API_ID) --user-pool-id $(npm config get COGNITO_USER_POOL_ID)", - "open-signup-page": "open \"https://$(npm config get API_ID).auth.$(aws cloudformation describe-stacks --stack-name $(npm config get STACK_NAME) --query 'Stacks[].Outputs[?OutputKey==`Region`].OutputValue' --output text).amazoncognito.com/signup?response_type=code&client_id=$(npm config get COGNITO_USER_POOL_CLIENT_ID)&redirect_uri=$(npm config get API_URL)\"", - "open-login-page": "open \"https://$(npm config get API_ID).auth.$(aws cloudformation describe-stacks --stack-name $(npm config get STACK_NAME) --query 'Stacks[].Outputs[?OutputKey==`Region`].OutputValue' --output text).amazoncognito.com/login?response_type=code&client_id=$(npm config get COGNITO_USER_POOL_CLIENT_ID)&redirect_uri=$(npm config get API_URL)\"", - "open-api-ui": "open \"$(npm config get API_URL)\"" - } -} diff --git a/examples/2016-10-31/api_cognito_auth/src/app.js b/examples/2016-10-31/api_cognito_auth/src/app.js deleted file mode 100644 index b37b1497b..000000000 --- a/examples/2016-10-31/api_cognito_auth/src/app.js +++ /dev/null @@ -1,79 +0,0 @@ -'use strict' -const express = require('express') -const bodyParser = require('body-parser') -const cors = require('cors') -const awsServerlessExpressMiddleware = require('aws-serverless-express/middleware') -const app = express() -const router = express.Router() - -app.set('view engine', 'pug') - -router.use(cors()) -router.use(bodyParser.json()) -router.use(bodyParser.urlencoded({ extended: true })) -router.use(awsServerlessExpressMiddleware.eventContext()) - -router.get('/', (req, res) => { - res.render('index', { - apiId: req.apiGateway ? req.apiGateway.event.requestContext.apiId : null, - apiUrl: req.apiGateway ? `https://${req.apiGateway.event.headers.Host}/${req.apiGateway.event.requestContext.stage}` : 'http://localhost:3000', - cognitoUserPoolClientId: process.env.COGNITO_USER_POOL_CLIENT_ID - }) -}) - -router.get('/users', (req, res) => { - res.json(users) -}) - -router.get('/users/:userId', (req, res) => { - const user = getUser(req.params.userId) - - if (!user) return res.status(404).json({}) - - return res.json(user) -}) - -router.post('/users', (req, res) => { - const user = { - id: ++userIdCounter, - name: req.body.name - } - users.push(user) - res.status(201).json(user) -}) - -router.put('/users/:userId', (req, res) => { - const user = getUser(req.params.userId) - - if (!user) return res.status(404).json({}) - - user.name = req.body.name - res.json(user) -}) - -router.delete('/users/:userId', (req, res) => { - const userIndex = getUserIndex(req.params.userId) - - if (userIndex === -1) return res.status(404).json({}) - - users.splice(userIndex, 1) - res.json(users) -}) - -const getUser = (userId) => users.find(u => u.id === parseInt(userId)) -const getUserIndex = (userId) => users.findIndex(u => u.id === parseInt(userId)) - -// Ephemeral in-memory data store -const users = [{ - id: 1, - name: 'Joe' -}, { - id: 2, - name: 'Jane' -}] -let userIdCounter = users.length - -app.use('/', router) - -// Export your express server so you can import it in the lambda function. -module.exports = app diff --git a/examples/2016-10-31/api_cognito_auth/src/lambda.js b/examples/2016-10-31/api_cognito_auth/src/lambda.js deleted file mode 100644 index 4f82597e7..000000000 --- a/examples/2016-10-31/api_cognito_auth/src/lambda.js +++ /dev/null @@ -1,7 +0,0 @@ -'use strict' -const awsServerlessExpress = require('aws-serverless-express') -const app = require('./app') - -const server = awsServerlessExpress.createServer(app) - -exports.handler = (event, context) => awsServerlessExpress.proxy(server, event, context) diff --git a/examples/2016-10-31/api_cognito_auth/src/views/index.pug b/examples/2016-10-31/api_cognito_auth/src/views/index.pug deleted file mode 100644 index 7634e6a9c..000000000 --- a/examples/2016-10-31/api_cognito_auth/src/views/index.pug +++ /dev/null @@ -1,159 +0,0 @@ -doctype html -html - head - title My Serverless Application - style. - body { - width: 650px; - margin: auto; - } - h1 { - text-align: center; - } - .response > code { - display: block; - background-color: #eff0f1; - color: #393318; - padding: 5px; - font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; - white-space: pre; - overflow-x: auto; - } - form { - margin-bottom: 1rem; - } - .form-group { - padding-bottom: 1rem; - } - label { - display: block; - } - body - h1 My Serverless Application - p - | Public endpoints: GET /, GET /users, GET /users/{userId} - p - | Authorized endpoints: POST /users, PUT /users/{userId}, DELETE /users/{userId} - - section.form - h2 Invoke API - p Experiment with the `users` resource with the form below. - form - div.form-group - label(for='methodField') Method - select(name='method' id='methodField') - option(value='GET') GET - option(value='POST') POST - option(value='PUT') PUT - option(value='DELETE') DELETE - div.form-group - label(for='idField') user id - input(type='text' name='id' id='idField') - div.form-group - label(for='nameField') name - input(type='text' name='name' id='nameField') - input(type='submit') - - section - h2 Response - p.request - span.request__method GET - span   - spand.request__endpoint /users - section.response - code - - script. - - const apiId = '#{apiId}' - const apiUrl = '#{apiUrl}/' - const cognitoUserPoolClientId = '#{cognitoUserPoolClientId}' - - const queryStringParams = new URLSearchParams(window.location.search) - const cognitoCode = queryStringParams.get('code') - let cognitoIdentityToken = localStorage.getItem('cognitoIdentityToken') - - const form = document.querySelector('form') - form.addEventListener('submit', onApiInvokeFormSubmit) - - fetch('users') - .then(setResponseText) - .catch(setResponseText) - - if (cognitoCode) { - exchangeCodeForAccessToken() - .then(response => response.json()) - .then(json => { - if (json.id_token) { - cognitoIdentityToken = json.id_token - localStorage.setItem('cognitoIdentityToken', cognitoIdentityToken) - } - }) - } - - function convertJsonToFormUrlEncoded(json) { - const oAuthTokenBodyArray = Object.entries(json).map(([key, value]) => { - const encodedKey = encodeURIComponent(key) - const encodedValue = encodeURIComponent(value) - - return `${encodedKey}=${encodedValue}` - }) - - return oAuthTokenBodyArray.join('&') - } - - function exchangeCodeForAccessToken() { - const oauthTokenBodyJson = { - grant_type: 'authorization_code', - client_id: cognitoUserPoolClientId, - code: cognitoCode, - redirect_uri: apiUrl - } - const oauthTokenBody = convertJsonToFormUrlEncoded(oauthTokenBodyJson) - - return fetch(`https://${apiId}.auth.us-east-1.amazoncognito.com/oauth2/token`, - { - method: 'POST', - headers: { - ['Content-Type']: 'application/x-www-form-urlencoded' - }, - body: oauthTokenBody - }) - } - - function onApiInvokeFormSubmit (event) { - event.preventDefault() - const method = document.getElementById('methodField').value - const id = document.getElementById('idField').value - const name = document.getElementById('nameField').value - const endpoint = id ? 'users/' + id : 'users' - const body = ['POST', 'PUT'].includes(method) ? JSON.stringify({ name: name }) : undefined - const headers = { - 'content-type': 'application/json', - 'Authorization': cognitoIdentityToken - } - - document.querySelector('.request__method').innerText = method - document.querySelector('.request__endpoint').innerText = `/${endpoint}` - - return fetch(endpoint, { - method, - headers, - body - }) - .then(setResponseText) - .catch(setResponseText) - } - - function setResponseText(response) { - const contentType = response.headers.get('content-type') - if (contentType.includes('application/json')) { - return response.json().then(json => { - document.querySelector('code').innerText = JSON.stringify(json, null, 4) - }) - } - - return response.text().then(text => { - document.querySelector('code').innerText = text - }) - } \ No newline at end of file diff --git a/examples/2016-10-31/api_cognito_auth/template.yaml b/examples/2016-10-31/api_cognito_auth/template.yaml deleted file mode 100644 index db06f35ef..000000000 --- a/examples/2016-10-31/api_cognito_auth/template.yaml +++ /dev/null @@ -1,151 +0,0 @@ -AWSTemplateFormatVersion: '2010-09-09' -Transform: AWS::Serverless-2016-10-31 -Description: API Gateway + Cognito User Pools Auth - -Parameters: - CognitoUserPoolName: - Type: String - Default: MyUserPool - - CognitoUserPoolClientName: - Type: String - Default: MyUserPoolClient - -Resources: - MyApi: - Type: AWS::Serverless::Api - Properties: - StageName: Prod - Cors: "'*'" - Auth: - DefaultAuthorizer: MyCognitoAuthorizer - Authorizers: - MyCognitoAuthorizer: - UserPoolArn: !GetAtt MyCognitoUserPool.Arn - - MyFunction: - Type: AWS::Serverless::Function - Properties: - CodeUri: ./src - Handler: lambda.handler - MemorySize: 1024 - Runtime: nodejs12.x - Environment: - Variables: - COGNITO_USER_POOL_CLIENT_ID: !Ref MyCognitoUserPoolClient - Events: - Root: - Type: Api - Properties: - RestApiId: !Ref MyApi - Path: / - Method: GET - # NOTE: This endpoint is publicly accessible - Auth: - Authorizer: NONE - ProxyAny: - Type: Api - Properties: - RestApiId: !Ref MyApi - Path: /{proxy+} - Method: ANY - # NOTE: This endpoint is publicly accessible - Auth: - Authorizer: NONE - GetUsers: - Type: Api - Properties: - RestApiId: !Ref MyApi - Path: /users - Method: GET - # NOTE: This endpoint is publicly accessible - Auth: - Authorizer: NONE - GetUser: - Type: Api - Properties: - RestApiId: !Ref MyApi - Path: /users/{userId} - Method: GET - # NOTE: This endpoint is publicly accessible - Auth: - Authorizer: NONE - CreateUser: - Type: Api - Properties: - RestApiId: !Ref MyApi - Path: /users - Method: POST - DeleteUser: - Type: Api - Properties: - RestApiId: !Ref MyApi - Path: /users/{userId} - Method: DELETE - UpdateUser: - Type: Api - Properties: - RestApiId: !Ref MyApi - Path: /users/{userId} - Method: PUT - - MyCognitoUserPool: - Type: AWS::Cognito::UserPool - Properties: - UserPoolName: !Ref CognitoUserPoolName - Policies: - PasswordPolicy: - MinimumLength: 8 - UsernameAttributes: - - email - Schema: - - AttributeDataType: String - Name: email - Required: false - - MyCognitoUserPoolClient: - Type: AWS::Cognito::UserPoolClient - Properties: - UserPoolId: !Ref MyCognitoUserPool - ClientName: !Ref CognitoUserPoolClientName - GenerateSecret: false - - PreSignupLambdaFunction: - Type: AWS::Serverless::Function - Properties: - InlineCode: | - exports.handler = async (event, context, callback) => { - event.response = { autoConfirmUser: true } - return event - } - Handler: index.handler - MemorySize: 128 - Runtime: nodejs12.x - Timeout: 3 - Events: - CognitoUserPoolPreSignup: - Type: Cognito - Properties: - UserPool: !Ref MyCognitoUserPool - Trigger: PreSignUp - -Outputs: - Region: - Description: "Region" - Value: !Ref AWS::Region - - ApiId: - Description: "API ID" - Value: !Ref MyApi - - ApiUrl: - Description: "API endpoint URL for Prod environment" - Value: !Sub 'https://${MyApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/' - - CognitoUserPoolId: - Description: "Cognito User Pool Id" - Value: !Ref MyCognitoUserPool - - CognitoUserPoolClientId: - Description: "Cognito User Pool Client Id" - Value: !Ref MyCognitoUserPoolClient \ No newline at end of file diff --git a/examples/2016-10-31/api_endpointconfiguration/template.yaml b/examples/2016-10-31/api_endpointconfiguration/template.yaml new file mode 100644 index 000000000..0d451aa53 --- /dev/null +++ b/examples/2016-10-31/api_endpointconfiguration/template.yaml @@ -0,0 +1,21 @@ +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 + +Description: Example demonstrating API EndpointConfiguration + +Parameters: + EndpointConfigType: + Type: String + Default: PRIVATE + VpcEndpointId: + Type: String + +Resources: + MyApi: + Type: AWS::Serverless::Api + Properties: + StageName: Prod + EndpointConfiguration: + Type: !Ref EndpointConfigType + VPCEndpointIds: + - !Ref VpcEndpointId \ No newline at end of file diff --git a/examples/2016-10-31/api_gateway_responses/template.yaml b/examples/2016-10-31/api_gateway_responses/template.yaml deleted file mode 100644 index 12df58618..000000000 --- a/examples/2016-10-31/api_gateway_responses/template.yaml +++ /dev/null @@ -1,33 +0,0 @@ -AWSTemplateFormatVersion: "2010-09-09" -Transform: AWS::Serverless-2016-10-31 -Description: Simple webservice deomnstrating gateway responses. - -Resources: - MyApi: - Type: AWS::Serverless::Api - Properties: - StageName: Prod - GatewayResponses: - DEFAULT_4xx: - ResponseParameters: - Headers: - Access-Control-Expose-Headers: "'WWW-Authenticate'" - Access-Control-Allow-Origin: "'*'" - - GetFunction: - Type: AWS::Serverless::Function - Properties: - Handler: index.get - Runtime: nodejs12.x - InlineCode: module.exports = async () => throw new Error('Check out the response headers!') - Events: - GetResource: - Type: Api - Properties: - Path: /error - Method: get - RestApiId: !Ref MyApi -Outputs: - ApiURL: - Description: "API endpoint URL for Prod environment" - Value: !Sub "https://${MyApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/error/" diff --git a/examples/2016-10-31/api_lambda_auth_cors/README.md b/examples/2016-10-31/api_lambda_auth_cors/README.md deleted file mode 100644 index 4e90fd825..000000000 --- a/examples/2016-10-31/api_lambda_auth_cors/README.md +++ /dev/null @@ -1,26 +0,0 @@ -# api_lambda_auth_cors - -## About - -This example shows how to configure a TOKEN Lambda Authorizer as the `DefaultAuthorizer` for an API with CORS enabled. - -## Installation - -1. Provide a bucket name and deploy the resources - ```bash - S3_BUCKET_NAME=your-bucket-name-here \ - npm run package-deploy - ``` -1. Install the required NPM dependencies - ```bash - npm install - ``` -1. Start the web server - ```bash - npm run start - ``` -1. Open `http://localhost:8080` in a browser, click the button and an alert will appear with the lambda response - -## Cleanup - -1. `aws cloudformation delete-stack --stack-name authorizer-cors-example` diff --git a/examples/2016-10-31/api_lambda_auth_cors/package-lock.json b/examples/2016-10-31/api_lambda_auth_cors/package-lock.json deleted file mode 100644 index f1ac6c63a..000000000 --- a/examples/2016-10-31/api_lambda_auth_cors/package-lock.json +++ /dev/null @@ -1,565 +0,0 @@ -{ - "name": "api_lambda_auth_cors", - "version": "1.0.0", - "lockfileVersion": 1, - "requires": true, - "dependencies": { - "@zeit/schemas": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@zeit/schemas/-/schemas-2.6.0.tgz", - "integrity": "sha512-uUrgZ8AxS+Lio0fZKAipJjAh415JyrOZowliZAzmnJSsf7piVL5w+G0+gFJ0KSu3QRhvui/7zuvpLz03YjXAhg==" - }, - "accepts": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", - "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", - "requires": { - "mime-types": "2.1.24", - "negotiator": "0.6.2" - } - }, - "ajv": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.3.tgz", - "integrity": "sha512-LqZ9wY+fx3UMiiPd741yB2pj3hhil+hQc8taf4o2QGRFpWgZ2V5C8HA165DY9sS3fJwsk7uT7ZlFEyC3Ig3lLg==", - "requires": { - "fast-deep-equal": "2.0.1", - "fast-json-stable-stringify": "2.0.0", - "json-schema-traverse": "0.4.1", - "uri-js": "4.2.2" - } - }, - "ansi-align": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-2.0.0.tgz", - "integrity": "sha1-w2rsy6VjuJzrVW82kPCx2eNUf38=", - "requires": { - "string-width": "2.1.1" - } - }, - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "requires": { - "color-convert": "1.9.3" - } - }, - "arch": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/arch/-/arch-2.1.1.tgz", - "integrity": "sha512-BLM56aPo9vLLFVa8+/+pJLnrZ7QGGTVHWsCwieAWT9o9K8UeGaQbzZbGoabWLOo2ksBCztoXdqBZBplqLDDCSg==" - }, - "arg": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/arg/-/arg-2.0.0.tgz", - "integrity": "sha512-XxNTUzKnz1ctK3ZIcI2XUPlD96wbHP2nGqkPKpvk/HNRlPveYrXIVSTk9m3LcqOgDPg3B1nMvdV/K8wZd7PG4w==" - }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" - }, - "boxen": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/boxen/-/boxen-1.3.0.tgz", - "integrity": "sha512-TNPjfTr432qx7yOjQyaXm3dSR0MH9vXp7eT1BFSl/C51g+EFnOR9hTg1IreahGBmDNCehscshe45f+C1TBZbLw==", - "requires": { - "ansi-align": "2.0.0", - "camelcase": "4.1.0", - "chalk": "2.4.1", - "cli-boxes": "1.0.0", - "string-width": "2.1.1", - "term-size": "1.2.0", - "widest-line": "2.0.1" - } - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "requires": { - "balanced-match": "1.0.0", - "concat-map": "0.0.1" - } - }, - "bytes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" - }, - "camelcase": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", - "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=" - }, - "chalk": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", - "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", - "requires": { - "ansi-styles": "3.2.1", - "escape-string-regexp": "1.0.5", - "supports-color": "5.5.0" - } - }, - "cli-boxes": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-1.0.0.tgz", - "integrity": "sha1-T6kXw+WclKAEzWH47lCdplFocUM=" - }, - "clipboardy": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/clipboardy/-/clipboardy-1.2.3.tgz", - "integrity": "sha512-2WNImOvCRe6r63Gk9pShfkwXsVtKCroMAevIbiae021mS850UkWPbevxsBz3tnvjZIEGvlwaqCPsw+4ulzNgJA==", - "requires": { - "arch": "2.1.1", - "execa": "0.8.0" - }, - "dependencies": { - "execa": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-0.8.0.tgz", - "integrity": "sha1-2NdrvBtVIX7RkP1t1J08d07PyNo=", - "requires": { - "cross-spawn": "5.1.0", - "get-stream": "3.0.0", - "is-stream": "1.1.0", - "npm-run-path": "2.0.2", - "p-finally": "1.0.0", - "signal-exit": "3.0.2", - "strip-eof": "1.0.0" - } - } - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" - }, - "compressible": { - "version": "2.0.17", - "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.17.tgz", - "integrity": "sha512-BGHeLCK1GV7j1bSmQQAi26X+GgWcTjLr/0tzSvMCl3LH1w1IJ4PFSPoV5316b30cneTziC+B1a+3OjoSUcQYmw==", - "requires": { - "mime-db": "1.40.0" - } - }, - "compression": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.3.tgz", - "integrity": "sha512-HSjyBG5N1Nnz7tF2+O7A9XUhyjru71/fwgNb7oIsEVHR0WShfs2tIS/EySLgiTe98aOK18YDlMXpzjCXY/n9mg==", - "requires": { - "accepts": "1.3.7", - "bytes": "3.0.0", - "compressible": "2.0.17", - "debug": "2.6.9", - "on-headers": "1.0.2", - "safe-buffer": "5.1.2", - "vary": "1.1.2" - } - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" - }, - "content-disposition": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", - "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=" - }, - "cross-spawn": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", - "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", - "requires": { - "lru-cache": "4.1.5", - "shebang-command": "1.2.0", - "which": "1.3.1" - } - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==" - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" - }, - "execa": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", - "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", - "requires": { - "cross-spawn": "5.1.0", - "get-stream": "3.0.0", - "is-stream": "1.1.0", - "npm-run-path": "2.0.2", - "p-finally": "1.0.0", - "signal-exit": "3.0.2", - "strip-eof": "1.0.0" - } - }, - "fast-deep-equal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", - "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=" - }, - "fast-json-stable-stringify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", - "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" - }, - "fast-url-parser": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/fast-url-parser/-/fast-url-parser-1.1.3.tgz", - "integrity": "sha1-9K8+qfNNiicc9YrSs3WfQx8LMY0=", - "requires": { - "punycode": "1.4.1" - }, - "dependencies": { - "punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" - } - } - }, - "get-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", - "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=" - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" - }, - "ini": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", - "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==" - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" - }, - "is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" - }, - "lru-cache": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", - "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", - "requires": { - "pseudomap": "1.0.2", - "yallist": "2.1.2" - } - }, - "mime-db": { - "version": "1.40.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", - "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==" - }, - "mime-types": { - "version": "2.1.24", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", - "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", - "requires": { - "mime-db": "1.40.0" - } - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "requires": { - "brace-expansion": "1.1.11" - } - }, - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "negotiator": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", - "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" - }, - "npm-run-path": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", - "requires": { - "path-key": "2.0.1" - } - }, - "on-headers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", - "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==" - }, - "p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" - }, - "path-is-inside": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", - "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=" - }, - "path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=" - }, - "path-to-regexp": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-2.2.1.tgz", - "integrity": "sha512-gu9bD6Ta5bwGrrU8muHzVOBFFREpp2iRkVfhBJahwJ6p6Xw20SjT0MxLnwkjOibQmGSYhiUnf2FLe7k+jcFmGQ==" - }, - "pseudomap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" - }, - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" - }, - "range-parser": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", - "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=" - }, - "rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "requires": { - "deep-extend": "0.6.0", - "ini": "1.3.5", - "minimist": "1.2.0", - "strip-json-comments": "2.0.1" - } - }, - "registry-auth-token": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.3.2.tgz", - "integrity": "sha512-JL39c60XlzCVgNrO+qq68FoNb56w/m7JYvGR2jT5iR1xBrUA3Mfx5Twk5rqTThPmQKMWydGmq8oFtDlxfrmxnQ==", - "requires": { - "rc": "1.2.8", - "safe-buffer": "5.1.2" - } - }, - "registry-url": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-3.1.0.tgz", - "integrity": "sha1-PU74cPc93h138M+aOBQyRE4XSUI=", - "requires": { - "rc": "1.2.8" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "serve": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/serve/-/serve-11.1.0.tgz", - "integrity": "sha512-+4wpDtOSS+4ZLyDWMxThutA3iOTawX2+yDovOI8cjOUOmemyvNlHyFAsezBlSgbZKTYChI3tzA1Mh0z6XZ62qA==", - "requires": { - "@zeit/schemas": "2.6.0", - "ajv": "6.5.3", - "arg": "2.0.0", - "boxen": "1.3.0", - "chalk": "2.4.1", - "clipboardy": "1.2.3", - "compression": "1.7.3", - "serve-handler": "6.1.0", - "update-check": "1.5.2" - } - }, - "serve-handler": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/serve-handler/-/serve-handler-6.1.0.tgz", - "integrity": "sha512-63N075Tn3PsFYcu0NVV7tb367UbiW3gnC+/50ohL4oqOhAG6bmbaWqiRcXQgbzqc0ALBjSAzg7VTfa0Qw4E3hA==", - "requires": { - "bytes": "3.0.0", - "content-disposition": "0.5.2", - "fast-url-parser": "1.1.3", - "mime-types": "2.1.18", - "minimatch": "3.0.4", - "path-is-inside": "1.0.2", - "path-to-regexp": "2.2.1", - "range-parser": "1.2.0" - }, - "dependencies": { - "mime-db": { - "version": "1.33.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz", - "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==" - }, - "mime-types": { - "version": "2.1.18", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz", - "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", - "requires": { - "mime-db": "1.33.0" - } - } - } - }, - "shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", - "requires": { - "shebang-regex": "1.0.0" - } - }, - "shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" - }, - "signal-exit": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "requires": { - "is-fullwidth-code-point": "2.0.0", - "strip-ansi": "4.0.0" - } - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "requires": { - "ansi-regex": "3.0.0" - } - }, - "strip-eof": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=" - }, - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "requires": { - "has-flag": "3.0.0" - } - }, - "term-size": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/term-size/-/term-size-1.2.0.tgz", - "integrity": "sha1-RYuDiH8oj8Vtb/+/rSYuJmOO+mk=", - "requires": { - "execa": "0.7.0" - } - }, - "update-check": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/update-check/-/update-check-1.5.2.tgz", - "integrity": "sha512-1TrmYLuLj/5ZovwUS7fFd1jMH3NnFDN1y1A8dboedIDt7zs/zJMo6TwwlhYKkSeEwzleeiSBV5/3c9ufAQWDaQ==", - "requires": { - "registry-auth-token": "3.3.2", - "registry-url": "3.1.0" - } - }, - "uri-js": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", - "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", - "requires": { - "punycode": "2.1.1" - } - }, - "vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" - }, - "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "requires": { - "isexe": "2.0.0" - } - }, - "widest-line": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-2.0.1.tgz", - "integrity": "sha512-Ba5m9/Fa4Xt9eb2ELXt77JxVDV8w7qQrH0zS/TWSJdLyAwQjWoOzpzj5lwVftDz6n/EOu3tNACS84v509qwnJA==", - "requires": { - "string-width": "2.1.1" - } - }, - "yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" - } - } -} diff --git a/examples/2016-10-31/api_lambda_auth_cors/package.json b/examples/2016-10-31/api_lambda_auth_cors/package.json deleted file mode 100644 index 39e48c880..000000000 --- a/examples/2016-10-31/api_lambda_auth_cors/package.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "name": "api_lambda_auth_cors", - "version": "1.0.0", - "description": "CORS enabled API Gateway that uses a Lambda Token Authorizer by default", - "license": "Apache-2.0", - "dependencies": { - "serve": "^11.1.0" - }, - "scripts": { - "deploy": "sam deploy --template-file packaged.yaml --stack-name authorizer-cors-example --capabilities CAPABILITY_IAM", - "get-url": "echo \"var helloWorldUrl = '$(aws cloudformation describe-stacks --stack-name authorizer-cors-example --query 'Stacks[].Outputs[?OutputKey==`HelloWorldUrl`].OutputValue')';\" > ./website/.env.js", - "package": "sam package --template-file template.yaml --output-template-file packaged.yaml --s3-bucket $S3_BUCKET_NAME", - "package-deploy": "npm run package && npm run deploy && npm run get-url", - "start": "serve -p 8080 website" - } -} \ No newline at end of file diff --git a/examples/2016-10-31/api_lambda_auth_cors/src/authorizer.js b/examples/2016-10-31/api_lambda_auth_cors/src/authorizer.js deleted file mode 100644 index 4a13423d2..000000000 --- a/examples/2016-10-31/api_lambda_auth_cors/src/authorizer.js +++ /dev/null @@ -1,44 +0,0 @@ -// adapted from ../../api_lambda_token_auth/src/authorizer.js - -exports.handler = async function (event) { - const token = event.authorizationToken.toLowerCase(); - const methodArn = event.methodArn; - - switch (token) { - case 'allow': - // allow the request - return generateAuthResponse('user123', 'Allow', methodArn); - case 'deny': - // return a 403 - return generateAuthResponse('user123', 'Deny', methodArn); - default: - // return a 500 - return Promise.reject('Error: Invalid token'); - } -}; - -/** - * NOTE: the authorizer caches policies by default across all methods and resources in a stage. - * If you are exposing more than one method or resource protected by the same authorizer, you may - * not want to use event.methodArn. - * - * If a caller uses the same, valid token to access two URLs, the first call will be successful - * but the second will fail because the cached policy only contains the ARN of the first method. - */ -function generateAuthResponse(principalId, effect, methodArn) { - if (!effect || !methodArn) return null; - - return { - principalId, - policyDocument: { - Version: '2012-10-17', - Statement: [ - { - Action: 'execute-api:Invoke', - Effect: effect, - Resource: methodArn - } - ] - } - }; -} diff --git a/examples/2016-10-31/api_lambda_auth_cors/src/index.js b/examples/2016-10-31/api_lambda_auth_cors/src/index.js deleted file mode 100644 index 869f74d9c..000000000 --- a/examples/2016-10-31/api_lambda_auth_cors/src/index.js +++ /dev/null @@ -1,11 +0,0 @@ -exports.handler = async event => { - return { - statusCode: 200, - headers: { - 'Access-Control-Allow-Origin': 'http://localhost:8080' - }, - body: JSON.stringify({ - message: 'hello world' - }) - }; -}; diff --git a/examples/2016-10-31/api_lambda_auth_cors/template.yaml b/examples/2016-10-31/api_lambda_auth_cors/template.yaml deleted file mode 100644 index f61f44ce3..000000000 --- a/examples/2016-10-31/api_lambda_auth_cors/template.yaml +++ /dev/null @@ -1,44 +0,0 @@ -AWSTemplateFormatVersion: '2010-09-09' -Transform: AWS::Serverless-2016-10-31 -Description: CORS enabled API Gateway that uses a Lambda TOKEN Authorizer by default - -Resources: - MyApi: - Type: AWS::Serverless::Api - Properties: - StageName: Prod - Auth: - DefaultAuthorizer: MyAuthorizer - AddDefaultAuthorizerToCorsPreflight: false # removes MyAuthorizer from the automatically created OPTIONS methods - Authorizers: - MyAuthorizer: - FunctionArn: !GetAtt AuthorizerFunction.Arn - Cors: - AllowHeaders: "'Authorization'" # this is the default header used by TOKEN authorizers - AllowOrigin: "'http://localhost:8080'" - - AuthorizerFunction: - Type: AWS::Serverless::Function - Properties: - CodeUri: src/ - Handler: authorizer.handler - Runtime: nodejs12.x - - HelloWorldFunction: - Type: AWS::Serverless::Function - Properties: - CodeUri: src/ - Handler: index.handler - Runtime: nodejs12.x - Events: - HelloWorld: - Type: Api - Properties: - RestApiId: !Ref MyApi - Path: / - Method: get - -Outputs: - HelloWorldUrl: - Description: 'HelloWorldFunction Prod stage URL' - Value: !Sub 'https://${MyApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/' diff --git a/examples/2016-10-31/api_lambda_auth_cors/website/app.js b/examples/2016-10-31/api_lambda_auth_cors/website/app.js deleted file mode 100644 index 3fe2f68c2..000000000 --- a/examples/2016-10-31/api_lambda_auth_cors/website/app.js +++ /dev/null @@ -1,10 +0,0 @@ -window.addEventListener('load', () => { - document.getElementById('call-api-btn').addEventListener('click', e => { - fetch(helloWorldUrl, { - method: 'get', - headers: { Authorization: 'allow' } - }) - .then(res => res.json()) - .then(resJson => alert(JSON.stringify(resJson))); - }); -}); diff --git a/examples/2016-10-31/api_lambda_auth_cors/website/index.html b/examples/2016-10-31/api_lambda_auth_cors/website/index.html deleted file mode 100644 index fb5b313e0..000000000 --- a/examples/2016-10-31/api_lambda_auth_cors/website/index.html +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - CORS + Authorizer - - - -
-

CORS + Default Authorizer

-
-
- -
- - - - - \ No newline at end of file diff --git a/examples/2016-10-31/api_lambda_request_auth/README.md b/examples/2016-10-31/api_lambda_request_auth/README.md deleted file mode 100644 index 841dee57c..000000000 --- a/examples/2016-10-31/api_lambda_request_auth/README.md +++ /dev/null @@ -1,47 +0,0 @@ -# API Gateway + Lambda REQUEST Authorizer Example - -This example shows you how to create an API Gateway API with a Lambda REQUEST Authorizer using SAM. - -The Authorizer Lambda Function in this example simply accepts an `auth` query string with valid values `"allow"` and `"deny"`. See [Introducing custom authorizers in Amazon API Gateway](https://aws.amazon.com/blogs/compute/introducing-custom-authorizers-in-amazon-api-gateway/) for a more detailed example using JWT. - -## Running the example - -Optional: Uncomment the following lines in `template.yaml` to enable a publicly accessible endpoint: - -```yaml -# Auth: -# Authorizer: NONE -``` - -Deploy the example into your account: - -```bash -# Replace YOUR_S3_ARTIFACTS_BUCKET with the name of a bucket which already exists in your account -aws cloudformation package --template-file template.yaml --output-template-file template.packaged.yaml --s3-bucket YOUR_S3_ARTIFACTS_BUCKET - -aws cloudformation deploy --template-file ./template.packaged.yaml --stack-name sam-example-api-lambda-request-auth --capabilities CAPABILITY_IAM -``` - -Invoke the API's root endpoint `/` without an `auth` query string to see the API respond with a 200 (assuming you followed the optional step above). In the SAM template, we explicitly state `Authorizer: NONE` to make this a public/open endpoint (the Authorizer Lambda Function is not invoked). - -```bash -api_url=$(aws cloudformation describe-stacks --stack-name sam-example-api-lambda-request-auth --query 'Stacks[].Outputs[?OutputKey==`ApiURL`].OutputValue' --output text) -curl $api_url -``` - -Invoke the API's `/users` endpoint without an `auth` query string to see a `401 {"message":"Unauthorized"}` response. Since we didn't specify an `auth` query string, API Gateway didn't even attempt to invoke our Authorizer Lambda Function. - -```bash -curl $api_url"users" -``` - -Invoke the API's `/users` endpoint with an `auth=allow` query string to see a `200` response. Try changing the query string value to `"deny"` to see a `403` response and `"gibberish"` to see a `500` response. - -```bash -curl $api_url"users?auth=allow" -``` - -## Additional resources - -- https://aws.amazon.com/blogs/compute/introducing-custom-authorizers-in-amazon-api-gateway/ -- https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-use-lambda-authorizer.html diff --git a/examples/2016-10-31/api_lambda_request_auth/src/authorizer.js b/examples/2016-10-31/api_lambda_request_auth/src/authorizer.js deleted file mode 100644 index 866177192..000000000 --- a/examples/2016-10-31/api_lambda_request_auth/src/authorizer.js +++ /dev/null @@ -1,58 +0,0 @@ -/* -** Adapted from https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-use-lambda-authorizer.html#api-gateway-lambda-authorizer-request-lambda-function-create -** A simple REQUEST authorizer example to demonstrate how to use request parameters to allow or deny a request. -** In this example, a request is authorized if the client provided an `auth=allow` in the query string. -*/ -exports.handler = async function (event) { - const token = event.queryStringParameters.auth.toLowerCase() - const methodArn = event.methodArn - - /* - ** headers, queryStringParameters, stageVariables, requestContext and more are available on the `event` object - ** event.headers: A map/object containing HTTP request headers - ** event.queryStringParameters: A map/object containing query strings supplied in the URL - ** event.stageVariables: A map/object containing variables defined on the API Gateway Stage - ** event.requestContext: A map/object containing additional request context - */ - - switch (token) { - case 'allow': - return generateAuthResponse('user', 'Allow', methodArn) - case 'deny': - return generateAuthResponse('user', 'Deny', methodArn) - default: - return Promise.reject('Error: Invalid token') // Returns 500 Internal Server Error - } -} - -function generateAuthResponse (principalId, effect, methodArn) { - // If you need to provide additional information to your integration - // endpoint (e.g. your Lambda Function), you can add it to `context` - const context = { - 'stringKey': 'stringval', - 'numberKey': 123, - 'booleanKey': true - } - const policyDocument = generatePolicyDocument(effect, methodArn) - - return { - principalId, - context, - policyDocument - } -} - -function generatePolicyDocument (effect, methodArn) { - if (!effect || !methodArn) return null - - const policyDocument = { - Version: '2012-10-17', - Statement: [{ - Action: 'execute-api:Invoke', - Effect: effect, - Resource: methodArn - }] - } - - return policyDocument -} diff --git a/examples/2016-10-31/api_lambda_request_auth/src/index.js b/examples/2016-10-31/api_lambda_request_auth/src/index.js deleted file mode 100644 index 472369c6c..000000000 --- a/examples/2016-10-31/api_lambda_request_auth/src/index.js +++ /dev/null @@ -1,7 +0,0 @@ -exports.handler = async (event) => { - return { - statusCode: 200, - body: JSON.stringify(event), - headers: {} - } -} diff --git a/examples/2016-10-31/api_lambda_request_auth/template.yaml b/examples/2016-10-31/api_lambda_request_auth/template.yaml deleted file mode 100644 index 9363c841d..000000000 --- a/examples/2016-10-31/api_lambda_request_auth/template.yaml +++ /dev/null @@ -1,61 +0,0 @@ -AWSTemplateFormatVersion: '2010-09-09' -Transform: AWS::Serverless-2016-10-31 -Description: API Gateway with Lambda Request Authorizer -Resources: - MyApi: - Type: AWS::Serverless::Api - Properties: - StageName: Prod - Auth: - DefaultAuthorizer: MyLambdaRequestAuthorizer - Authorizers: - MyLambdaRequestAuthorizer: - FunctionPayloadType: REQUEST - FunctionArn: !GetAtt MyAuthFunction.Arn - # FunctionInvokeRole: !Ref MyRole - Identity: - QueryStrings: - - auth - # NOTE: Additional options: - # Headers: - # - Authorization - # StageVariables: - # - AUTHORIZATION - # Context: - # - authorization - # ReauthorizeEvery: 100 # seconds - - MyFunction: - Type: AWS::Serverless::Function - Properties: - CodeUri: ./src - Handler: index.handler - Runtime: nodejs12.x - Events: - GetRoot: - Type: Api - Properties: - RestApiId: !Ref MyApi - Path: / - Method: get - # NOTE: Uncomment the two lines below to make `GET /` publicly accessible - # Auth: - # Authorizer: NONE - GetUsers: - Type: Api - Properties: - RestApiId: !Ref MyApi - Path: /users - Method: get - - MyAuthFunction: - Type: AWS::Serverless::Function - Properties: - CodeUri: ./src - Handler: authorizer.handler - Runtime: nodejs12.x - -Outputs: - ApiURL: - Description: "API URL" - Value: !Sub 'https://${MyApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/' diff --git a/examples/2016-10-31/api_lambda_token_auth/README.md b/examples/2016-10-31/api_lambda_token_auth/README.md deleted file mode 100644 index 02504b6e9..000000000 --- a/examples/2016-10-31/api_lambda_token_auth/README.md +++ /dev/null @@ -1,46 +0,0 @@ -# API Gateway + Lambda TOKEN Authorizer Example - -This example shows you how to create an API Gateway API with a Lambda TOKEN Authorizer using SAM. - -The Authorizer Lambda Function in this example simply accepts an `Authorization` header with valid values `"allow"` and `"deny"`. See [Introducing custom authorizers in Amazon API Gateway](https://aws.amazon.com/blogs/compute/introducing-custom-authorizers-in-amazon-api-gateway/) for a more detailed example using JWT. - -## Running the example - -Optional: Uncomment the following lines in `template.yaml` to enable a publicly accessible endpoint: - -```yaml -# Auth: -# Authorizer: NONE -``` - -Deploy the example into your account: - -```bash -# Replace YOUR_S3_ARTIFACTS_BUCKET with the name of a bucket which already exists in your account -aws cloudformation package --template-file template.yaml --output-template-file template.packaged.yaml --s3-bucket YOUR_S3_ARTIFACTS_BUCKET - -aws cloudformation deploy --template-file ./template.packaged.yaml --stack-name sam-example-api-lambda-token-auth --capabilities CAPABILITY_IAM -``` - -Invoke the API's root endpoint `/` without an `Authorization` header to see the API respond with a 200 (assuming you followed the optional step above). In the SAM template, we explicitly state `Authorizer: NONE` to make this a public/open endpoint (the Authorizer Lambda Function is not invoked). - -```bash -curl "$(aws cloudformation describe-stacks --stack-name sam-example-api-lambda-token-auth --query 'Stacks[].Outputs[?OutputKey==`ApiURL`].OutputValue' --output text)" -``` - -Invoke the API's `/users` endpoint without an `Authorization` header to see a `401 {"message":"Unauthorized"}` response. Since we didn't specify an `Authorization` header, API Gateway didn't even attempt to invoke our Authorizer Lambda Function. If you specify a `ValidationExpression` on the Authorizer, it will also not invoke the Authorizer Lambda Function if the header was provided but does not match the pattern. - -```bash -curl "$(aws cloudformation describe-stacks --stack-name sam-example-api-lambda-token-auth --query 'Stacks[].Outputs[?OutputKey==`ApiURL`].OutputValue' --output text)users" -``` - -Invoke the API's `/users` endpoint with an `Authorization: allow` header to see a `200` response. Try changing the header value to `"deny"` to see a `403` response and `"gibberish"` to see a `500` response. - -```bash -curl "$(aws cloudformation describe-stacks --stack-name sam-example-api-lambda-token-auth --query 'Stacks[].Outputs[?OutputKey==`ApiURL`].OutputValue' --output text)users" -H 'Authorization: allow' -``` - -## Additional resources - -- https://aws.amazon.com/blogs/compute/introducing-custom-authorizers-in-amazon-api-gateway/ -- https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-use-lambda-authorizer.html diff --git a/examples/2016-10-31/api_lambda_token_auth/src/authorizer.js b/examples/2016-10-31/api_lambda_token_auth/src/authorizer.js deleted file mode 100644 index b5bceb1be..000000000 --- a/examples/2016-10-31/api_lambda_token_auth/src/authorizer.js +++ /dev/null @@ -1,55 +0,0 @@ -/* -** Adapted from https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-use-lambda-authorizer.html#api-gateway-lambda-authorizer-token-lambda-function-create -** A simple TOKEN authorizer example to demonstrate how to use an authorization token -** to allow or deny a request.In this example, the caller named 'user'is allowed to invoke -** a request if the client - supplied token value is 'allow'.The caller is not allowed to -** invoke the request if the token value is 'deny'.If the token value is 'Unauthorized', -** the function returns the 'Unauthorized' error with an HTTP status code of 401. For any -** other token value, the authorizer returns 'Error: Invalid token' as a 500 error. -*/ - -exports.handler = async function (event) { - const token = event.authorizationToken.toLowerCase() - const methodArn = event.methodArn - - switch (token) { - case 'allow': - return generateAuthResponse('user', 'Allow', methodArn) - case 'deny': - return generateAuthResponse('user', 'Deny', methodArn) - default: - return Promise.reject('Error: Invalid token') // Returns 500 Internal Server Error - } -} - -function generateAuthResponse (principalId, effect, methodArn) { - // If you need to provide additional information to your integration - // endpoint (e.g. your Lambda Function), you can add it to `context` - const context = { - 'stringKey': 'stringval', - 'numberKey': 123, - 'booleanKey': true - } - const policyDocument = generatePolicyDocument(effect, methodArn) - - return { - principalId, - context, - policyDocument - } -} - -function generatePolicyDocument (effect, methodArn) { - if (!effect || !methodArn) return null - - const policyDocument = { - Version: '2012-10-17', - Statement: [{ - Action: 'execute-api:Invoke', - Effect: effect, - Resource: methodArn - }] - } - - return policyDocument -} diff --git a/examples/2016-10-31/api_lambda_token_auth/src/index.js b/examples/2016-10-31/api_lambda_token_auth/src/index.js deleted file mode 100644 index d33f8abad..000000000 --- a/examples/2016-10-31/api_lambda_token_auth/src/index.js +++ /dev/null @@ -1,7 +0,0 @@ -exports.handler = async (event) => { - return { - statusCode: 200, - body: JSON.stringify(event), - headers: {} - } -} \ No newline at end of file diff --git a/examples/2016-10-31/api_lambda_token_auth/template.yaml b/examples/2016-10-31/api_lambda_token_auth/template.yaml deleted file mode 100644 index be285b364..000000000 --- a/examples/2016-10-31/api_lambda_token_auth/template.yaml +++ /dev/null @@ -1,54 +0,0 @@ -AWSTemplateFormatVersion: '2010-09-09' -Transform: AWS::Serverless-2016-10-31 -Description: API Gateway with Lambda Token Authorizer -Resources: - MyApi: - Type: AWS::Serverless::Api - Properties: - StageName: Prod - Auth: - DefaultAuthorizer: MyLambdaTokenAuthorizer - Authorizers: - MyLambdaTokenAuthorizer: - FunctionArn: !GetAtt MyAuthFunction.Arn - # NOTE: Additional options: - # FunctionInvokeRole: !Ref MyRole - # Identity: - # Header: Auth - # ValidationExpression: Bearer.* - # ReauthorizeEvery: 30 # seconds - - MyFunction: - Type: AWS::Serverless::Function - Properties: - CodeUri: ./src - Handler: index.handler - Runtime: nodejs12.x - Events: - GetRoot: - Type: Api - Properties: - RestApiId: !Ref MyApi - Path: / - Method: get - # NOTE: Uncomment the two lines below to make `GET /` publicly accessible - # Auth: - # Authorizer: NONE - GetUsers: - Type: Api - Properties: - RestApiId: !Ref MyApi - Path: /users - Method: get - - MyAuthFunction: - Type: AWS::Serverless::Function - Properties: - CodeUri: ./src - Handler: authorizer.handler - Runtime: nodejs12.x - -Outputs: - ApiURL: - Description: "API URL" - Value: !Sub 'https://${MyApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/' \ No newline at end of file diff --git a/examples/2016-10-31/api_request_model/src/index.js b/examples/2016-10-31/api_request_model/src/index.js deleted file mode 100644 index 7f7a6018d..000000000 --- a/examples/2016-10-31/api_request_model/src/index.js +++ /dev/null @@ -1,6 +0,0 @@ -exports.handler = function(event, context, callback) { - callback(null, { - "statusCode": 200, - "body": "hello world" - }); -} diff --git a/examples/2016-10-31/api_request_model/template.yaml b/examples/2016-10-31/api_request_model/template.yaml deleted file mode 100644 index c16a6828a..000000000 --- a/examples/2016-10-31/api_request_model/template.yaml +++ /dev/null @@ -1,46 +0,0 @@ -AWSTemplateFormatVersion: '2010-09-09' -Transform: AWS::Serverless-2016-10-31 -Description: Simple API Endpoint configured using Swagger specified inline and backed by a Lambda function -Resources: - MyApi: - Type: AWS::Serverless::Api - Properties: - StageName: prod - Models: - User: - type: object - required: - - grant_type - - username - - password - properties: - grant_type: - type: string - username: - type: string - password: - type: string - - MyLambdaFunction: - Type: AWS::Serverless::Function - Properties: - Handler: index.handler - Runtime: nodejs12.x - CodeUri: src/ - Events: - GetApi: - Type: Api - Properties: - Path: /post - Method: POST - RestApiId: - Ref: MyApi - RequestModel: - Model: User - Required: true - -Outputs: - - ApiURL: - Description: "API endpoint URL for Prod environment" - Value: !Sub "https://${MyApi}.execute-api.${AWS::Region}.amazonaws.com/prod/get" \ No newline at end of file diff --git a/examples/2016-10-31/api_resource_policy/README.md b/examples/2016-10-31/api_resource_policy/README.md deleted file mode 100644 index 7222c1290..000000000 --- a/examples/2016-10-31/api_resource_policy/README.md +++ /dev/null @@ -1,11 +0,0 @@ -# Api Resource Policy Event Source Example - -Example SAM template for adding Custom Resource Policy to Api. - -## Running the example - -```bash -# Replace YOUR_S3_ARTIFACTS_BUCKET -aws cloudformation package --template-file template.yaml --output-template-file cfn-transformed-template.yaml --s3-bucket YOUR_S3_ARTIFACTS_BUCKET -aws cloudformation deploy --template-file ./cfn-transformed-template.yaml --stack-name example-resource-policy --capabilities CAPABILITY_IAM -``` diff --git a/examples/2016-10-31/api_resource_policy/template.yaml b/examples/2016-10-31/api_resource_policy/template.yaml deleted file mode 100644 index 6194a9b56..000000000 --- a/examples/2016-10-31/api_resource_policy/template.yaml +++ /dev/null @@ -1,52 +0,0 @@ -AWSTemplateFormatVersion: '2010-09-09' -Transform: AWS::Serverless-2016-10-31 -Conditions: - C1: - Fn::Equals: - - true - - true -Globals: - Api: - Auth: - ResourcePolicy: - CustomStatements: - Fn::If: - - C1 - - Principal: '*' - Effect: Allow - Action: execute-api:Invoke - Resource: - - execute-api:/Prod/PUT/get - Condition: - IpAddress: - aws:SourceIp: 1.2.3.4 - - Principal: '*' - Effect: Allow - Action: execute-api:Invoke - Resource: - - execute-api:/Prod/PUT/get - Condition: - IpAddress: - aws:SourceIp: 5.6.7.8 - # OR you can use the following - # IpRangeBlacklist: ['1.2.3.4'] -Resources: - MyFunction: - Type: AWS::Serverless::Function - Properties: - InlineCode: | - exports.handler = async (event) => { - const response = { - statusCode: 200, - body: JSON.stringify('Hello from Lambda!'), - }; - return response; - }; - Handler: index.handler - Runtime: nodejs12.x - Events: - Api: - Type: Api - Properties: - Method: Put - Path: /get diff --git a/examples/2016-10-31/api_swagger_cors/index.js b/examples/2016-10-31/api_swagger_cors/index.js deleted file mode 100644 index 73f602ace..000000000 --- a/examples/2016-10-31/api_swagger_cors/index.js +++ /dev/null @@ -1,16 +0,0 @@ -exports.handler = function(event, context, callback) { - - callback(null, { - statusCode: '200', - body: "Hello world", - headers: { - // This is ALSO required for CORS to work. When browsers issue cross origin requests, they make a - // preflight request (HTTP Options) which is responded automatically based on SAM configuration. - // But the actual HTTP request (GET/POST etc) also needs to contain the AllowOrigin header. - // - // NOTE: This value is *not* double quoted: ie. "'www.example.com'" is wrong - "Access-Control-Allow-Origin": "https://www.example.com" - } - }); - -} diff --git a/examples/2016-10-31/api_swagger_cors/swagger.yaml b/examples/2016-10-31/api_swagger_cors/swagger.yaml deleted file mode 100644 index 05d7ea4ed..000000000 --- a/examples/2016-10-31/api_swagger_cors/swagger.yaml +++ /dev/null @@ -1,49 +0,0 @@ ---- -swagger: 2.0 -basePath: /prod -info: - title: AwsSamExample -schemes: -- https -paths: - /: - x-amazon-apigateway-any-method: - produces: - - application/json - responses: - '200': - description: 200 response - schema: - $ref: "#/definitions/Empty" - x-amazon-apigateway-integration: - responses: - default: - statusCode: 200 - # NOTE: ${LambdaFunction} must match the Lambda resourcename - uri: - Fn::Sub: "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${LambdaFunction.Arn}/invocations" - - passthroughBehavior: when_no_match - httpMethod: POST # Keep "POST" when the API definition method is not POST. This "httpMethod" is used to call Lambda. - type: aws_proxy - /{proxy+}: - x-amazon-apigateway-any-method: - x-amazon-apigateway-auth: - type: aws_iam - produces: - - application/json - parameters: - - name: proxy - in: path - required: true - type: string - responses: {} - x-amazon-apigateway-integration: - uri: - Fn::Sub: "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${LambdaFunction.Arn}/invocations" - httpMethod: POST # Keep "POST" when the API definition method is not POST. This "httpMethod" is used to call Lambda. - type: aws_proxy -definitions: - Empty: - type: object - title: Empty Schema diff --git a/examples/2016-10-31/api_swagger_cors/template.yaml b/examples/2016-10-31/api_swagger_cors/template.yaml deleted file mode 100644 index a386bae23..000000000 --- a/examples/2016-10-31/api_swagger_cors/template.yaml +++ /dev/null @@ -1,52 +0,0 @@ ---- -AWSTemplateFormatVersion: '2010-09-09' -Transform: AWS::Serverless-2016-10-31 -Description: AWS SAM template with API defined in an external Swagger file along with Lambda integrations and CORS configurations -Resources: - ApiGatewayApi: - Type: AWS::Serverless::Api - Properties: - StageName: Prod - - # Allows www.example.com to call these APIs - # SAM will automatically add AllowMethods with a list of methods for this API - Cors: "'www.example.com'" - - DefinitionBody: - 'Fn::Transform': - Name: 'AWS::Include' - # Replace with your bucket name - Parameters: - Location: s3:///swagger.yaml - - LambdaFunction: - Type: AWS::Serverless::Function - Properties: - # Replace with your bucket name - CodeUri: s3:///api_swagger_cors.zip - Handler: index.handler - Runtime: nodejs12.x - Events: - ProxyApiRoot: - Type: Api - Properties: - RestApiId: !Ref ApiGatewayApi - Path: / - Method: ANY - ProxyApiGreedy: - Type: Api - Properties: - RestApiId: !Ref ApiGatewayApi - Path: /{proxy+} - Method: ANY - -Outputs: - ApiUrl: - Description: URL of your API endpoint - Value: !Join - - '' - - - https:// - - !Ref ApiGatewayApi - - '.execute-api.' - - !Ref 'AWS::Region' - - '.amazonaws.com/Prod' diff --git a/examples/2016-10-31/cloudwatch-event-to-msteams/readme b/examples/2016-10-31/cloudwatch-event-to-msteams/readme deleted file mode 100644 index 1976bc0aa..000000000 --- a/examples/2016-10-31/cloudwatch-event-to-msteams/readme +++ /dev/null @@ -1,3 +0,0 @@ -CloudWatch Events has been re-launched as Amazon EventBridge with full backwards compatibility - -Please see ../eventbridge-event-to-msteams/ for the migrated code \ No newline at end of file diff --git a/examples/2016-10-31/cloudwatch-logs/.gitignore b/examples/2016-10-31/cloudwatch-logs/.gitignore deleted file mode 100644 index 97902fce3..000000000 --- a/examples/2016-10-31/cloudwatch-logs/.gitignore +++ /dev/null @@ -1 +0,0 @@ -transformed-cfn-template.yaml \ No newline at end of file diff --git a/examples/2016-10-31/cloudwatch-logs/README.md b/examples/2016-10-31/cloudwatch-logs/README.md deleted file mode 100644 index c65a9d096..000000000 --- a/examples/2016-10-31/cloudwatch-logs/README.md +++ /dev/null @@ -1,19 +0,0 @@ -# CloudWatchLogs Event Source Example - -Example SAM template for processing CloudWatch Logs. - -## Running the example - -```bash -# Replace YOUR_S3_ARTIFACTS_BUCKET -aws cloudformation package --template-file template.yaml --output-template-file cfn-transformed-template.yaml --s3-bucket YOUR_S3_ARTIFACTS_BUCKET -aws cloudformation deploy --template-file ./cfn-transformed-template.yaml --stack-name example-logs-processor --capabilities CAPABILITY_IAM -``` - -After your CloudFormation Stack has completed creation, log an event to your CloudWatch Log Group + Stream. To see it in action, modify and run the command below: - -```bash -# Replace YOUR_LOG_GROUP_NAME with the name generated by CloudFormation (e.g. example-logs-processor-CloudWatchLambdaLogsGroup-XXXXXX) -UNIX_TIME=$(date +%s) LOG_TIME=$((UNIX_TIME*1000)) && aws logs put-log-events --log-group-name "YOUR_LOG_GROUP_NAME" --log-stream-name "sam-log-stream" --log-events "[{\"message\": \"This won't be processed since it doesn't match the FilterPattern.\", \"timestamp\": $LOG_TIME}, {\"message\": \"This will be processed since it says Hello log processor.\", \"timestamp\": $LOG_TIME}]" -# NOTE: Subsequent logs will require adding `--sequence-token SEQUENCE_TOKEN` to the command -``` \ No newline at end of file diff --git a/examples/2016-10-31/cloudwatch-logs/index.js b/examples/2016-10-31/cloudwatch-logs/index.js deleted file mode 100644 index 3bb684570..000000000 --- a/examples/2016-10-31/cloudwatch-logs/index.js +++ /dev/null @@ -1,9 +0,0 @@ -function handler (event, context, callback) { - // TODO: Process logs... - - console.log(event) - - callback(null, {}) -} - -module.exports.handler = handler \ No newline at end of file diff --git a/examples/2016-10-31/cloudwatch-logs/template.yaml b/examples/2016-10-31/cloudwatch-logs/template.yaml deleted file mode 100644 index feece9270..000000000 --- a/examples/2016-10-31/cloudwatch-logs/template.yaml +++ /dev/null @@ -1,27 +0,0 @@ -AWSTemplateFormatVersion: '2010-09-09' -Transform: AWS::Serverless-2016-10-31 -Description: Example of processing CloudWatch Logs with Lambda -Resources: - LogsFunction: - Type: AWS::Serverless::Function - Properties: - CodeUri: ./index.js - Handler: index.handler - Runtime: nodejs12.x - Events: - LogsProcessor: - Type: CloudWatchLogs - Properties: - LogGroupName: !Ref CloudWatchLambdaLogsGroup - FilterPattern: Hello log processor - - CloudWatchLambdaLogsGroup: - Type: AWS::Logs::LogGroup - Properties: - RetentionInDays: 7 - - CloudWatchLambdaLogsStream: - Type: AWS::Logs::LogStream - Properties: - LogGroupName: !Ref CloudWatchLambdaLogsGroup - LogStreamName: sam-log-stream \ No newline at end of file diff --git a/examples/2016-10-31/custom_domains_with_route53/README.md b/examples/2016-10-31/custom_domains_with_route53/README.md deleted file mode 100644 index 6b66c0ea7..000000000 --- a/examples/2016-10-31/custom_domains_with_route53/README.md +++ /dev/null @@ -1,22 +0,0 @@ -# Custom Domains support - -Example SAM template for setting up Api Gateway resources for custom domains. - -## Prerequisites for setting up custom domains -1. A domain name. You can purchase a domain name from a domain name provider. -1. A certificate ARN. Set up or import a valid certificate into AWS Certificate Manager. If the endpoint is EDGE, the certificate must be created in us-east-1. -1. A HostedZone in Route53 for the domain name. - -## PostRequisites -After deploying the template, make sure you configure the DNS settings on the domain name provider's website. You will need to add Type A and Type AAAA DNS records that are point to ApiGateway's Hosted Zone Id. Read more [here](https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/routing-to-api-gateway.html) - -## Running the example - -```bash -$ sam deploy \ - --template-file /path_to_template/packaged-template.yaml \ - --stack-name my-new-stack \ - --capabilities CAPABILITY_IAM -``` - -Curl to the endpoint "http://example.com/home/fetch" should hit the Api. \ No newline at end of file diff --git a/examples/2016-10-31/custom_domains_with_route53/template.yaml b/examples/2016-10-31/custom_domains_with_route53/template.yaml deleted file mode 100644 index b9b7dbb7d..000000000 --- a/examples/2016-10-31/custom_domains_with_route53/template.yaml +++ /dev/null @@ -1,71 +0,0 @@ -Parameters: - DomainName: - Type: String - Default: 'example.com' - ACMCertificateArn: - Type: String - Default: 'cert-arn-in-us-east-1' -Resources: - MyFunction: - Type: AWS::Serverless::Function - Properties: - InlineCode: | - exports.handler = async (event) => { - const response = { - statusCode: 200, - body: JSON.stringify('Hello from Lambda!'), - }; - return response; - }; - Handler: index.handler - Runtime: nodejs12.x - Events: - Fetch: - Type: Api - Properties: - RestApiId: !Ref MyApi - Method: Post - Path: /fetch - - MyApi: - Type: AWS::Serverless::Api # Also works with HTTP API - Properties: - OpenApiVersion: 3.0.1 - StageName: Prod - Domain: - DomainName: !Ref DomainName - CertificateArn: !Ref ACMCertificateArn - EndpointConfiguration: EDGE - BasePath: - - /home - Route53: - HostedZoneId: ZQ1UAL4EFZVME - IpV6: true -## ====== Everything below here is optional, leave this out if you want to use the internal Api Gateway distribution ======= - DistributionDomainName: !GetAtt Distribution.DomainName - - Distribution: - Type: AWS::CloudFront::Distribution - Properties: - DistributionConfig: - Enabled: true - HttpVersion: http2 - Origins: - - DomainName: !Ref DomainName - Id: !Ref DomainName - CustomOriginConfig: - HTTPPort: 80 - HTTPSPort: 443 - OriginProtocolPolicy: https-only - DefaultCacheBehavior: - AllowedMethods: [ HEAD, DELETE, POST, GET, OPTIONS, PUT, PATCH ] - ForwardedValues: - QueryString: false - SmoothStreaming: false - Compress: true - TargetOriginId: !Ref DomainName - ViewerProtocolPolicy: redirect-to-https - PriceClass: PriceClass_100 - ViewerCertificate: - SslSupportMethod: sni-only - AcmCertificateArn: !Ref ACMCertificateArn \ No newline at end of file diff --git a/examples/2016-10-31/custom_domains_with_route53_hosted_zone_name/README.md b/examples/2016-10-31/custom_domains_with_route53_hosted_zone_name/README.md deleted file mode 100644 index 6b66c0ea7..000000000 --- a/examples/2016-10-31/custom_domains_with_route53_hosted_zone_name/README.md +++ /dev/null @@ -1,22 +0,0 @@ -# Custom Domains support - -Example SAM template for setting up Api Gateway resources for custom domains. - -## Prerequisites for setting up custom domains -1. A domain name. You can purchase a domain name from a domain name provider. -1. A certificate ARN. Set up or import a valid certificate into AWS Certificate Manager. If the endpoint is EDGE, the certificate must be created in us-east-1. -1. A HostedZone in Route53 for the domain name. - -## PostRequisites -After deploying the template, make sure you configure the DNS settings on the domain name provider's website. You will need to add Type A and Type AAAA DNS records that are point to ApiGateway's Hosted Zone Id. Read more [here](https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/routing-to-api-gateway.html) - -## Running the example - -```bash -$ sam deploy \ - --template-file /path_to_template/packaged-template.yaml \ - --stack-name my-new-stack \ - --capabilities CAPABILITY_IAM -``` - -Curl to the endpoint "http://example.com/home/fetch" should hit the Api. \ No newline at end of file diff --git a/examples/2016-10-31/custom_domains_with_route53_hosted_zone_name/template.yaml b/examples/2016-10-31/custom_domains_with_route53_hosted_zone_name/template.yaml deleted file mode 100644 index 144d8619e..000000000 --- a/examples/2016-10-31/custom_domains_with_route53_hosted_zone_name/template.yaml +++ /dev/null @@ -1,71 +0,0 @@ -Parameters: - DomainName: - Type: String - Default: 'example.com' - ACMCertificateArn: - Type: String - Default: 'cert-arn-in-us-east-1' -Resources: - MyFunction: - Type: AWS::Serverless::Function - Properties: - InlineCode: | - exports.handler = async (event) => { - const response = { - statusCode: 200, - body: JSON.stringify('Hello from Lambda!'), - }; - return response; - }; - Handler: index.handler - Runtime: nodejs8.10 - Events: - Fetch: - Type: Api - Properties: - RestApiId: !Ref MyApi - Method: Post - Path: /fetch - - MyApi: - Type: AWS::Serverless::Api - Properties: - OpenApiVersion: 3.0.1 - StageName: Prod - Domain: - DomainName: !Ref DomainName - CertificateArn: !Ref ACMCertificateArn - EndpointConfiguration: EDGE - BasePath: - - /home - Route53: - HostedZoneName: www.my-domain.com. - IpV6: true -## ====== Everything below here is optional, leave this out if you want to use the internal Api Gateway distribution ======= - DistributionDomainName: !GetAtt Distribution.DomainName - - Distribution: - Type: AWS::CloudFront::Distribution - Properties: - DistributionConfig: - Enabled: true - HttpVersion: http2 - Origins: - - DomainName: !Ref DomainName - Id: !Ref DomainName - CustomOriginConfig: - HTTPPort: 80 - HTTPSPort: 443 - OriginProtocolPolicy: https-only - DefaultCacheBehavior: - AllowedMethods: [ HEAD, DELETE, POST, GET, OPTIONS, PUT, PATCH ] - ForwardedValues: - QueryString: false - SmoothStreaming: false - Compress: true - TargetOriginId: !Ref DomainName - ViewerProtocolPolicy: redirect-to-https - PriceClass: PriceClass_100 - ViewerCertificate: - SslSupportMethod: sni-only - AcmCertificateArn: !Ref ACMCertificateArn \ No newline at end of file diff --git a/examples/2016-10-31/custom_domains_without_route53/README.md b/examples/2016-10-31/custom_domains_without_route53/README.md deleted file mode 100644 index 68315db15..000000000 --- a/examples/2016-10-31/custom_domains_without_route53/README.md +++ /dev/null @@ -1,19 +0,0 @@ -# Custom Domains support - -Example SAM template for setting up Api Gateway resources for custom domains. - -## Prerequisites for setting up custom domains -1. A domain name. You can purchase a domain name from a domain name provider. -1. A certificate ARN. Set up or import a valid certificate into AWS Certificate Manager. - -## PostRequisites -After deploying the template, make sure you configure the DNS settings on the domain name provider's website. You will need to add Type A and Type AAAA DNS records that are point to ApiGateway's Hosted Zone Id. Read more [here](https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/routing-to-api-gateway.html) - -## Running the example - -```bash -$ sam deploy \ - --template-file /path_to_template/packaged-template.yaml \ - --stack-name my-new-stack \ - --capabilities CAPABILITY_IAM -``` diff --git a/examples/2016-10-31/custom_domains_without_route53/template.yaml b/examples/2016-10-31/custom_domains_without_route53/template.yaml deleted file mode 100644 index 5d58fd848..000000000 --- a/examples/2016-10-31/custom_domains_without_route53/template.yaml +++ /dev/null @@ -1,37 +0,0 @@ -Parameters: - MyDomainName: - Type: String - Default: another-example.com - - MyDomainCert: - Type: String - Default: another-api-arn - -Globals: - Api: - Domain: - DomainName: !Ref MyDomainName - CertificateArn: !Ref MyDomainCert - EndpointConfiguration: 'REGIONAL' - BasePath: ['/get'] - -Resources: - MyFunction: - Type: AWS::Serverless::Function - Properties: - InlineCode: | - exports.handler = async (event) => { - const response = { - statusCode: 200, - body: JSON.stringify('Hello from Lambda!'), - }; - return response; - }; - Handler: index.handler - Runtime: nodejs12.x - Events: - ImplicitGet: - Type: Api - Properties: - Method: Get - Path: /get diff --git a/examples/2016-10-31/encryption_proxy/README.MD b/examples/2016-10-31/encryption_proxy/README.MD deleted file mode 100644 index 49da4c341..000000000 --- a/examples/2016-10-31/encryption_proxy/README.MD +++ /dev/null @@ -1,47 +0,0 @@ -### Encryption/Decryption Proxy - -This template creates the following resources: - -* 2 Lambda functions (Encryption, Decryption) - - SAM takes care of creating 2 stages API Gateway {Stage,Prod} that sits on top -* KMS Key and KMS Decrypt/Encrypt permissions for each IAM Role separately - - KMS Key ID is then passed to Encryption Lambda via Env vars - - -#### Encrypt Action - -* Consumes up to 4KB of data (string, json blob, etc.) -* Returns encrypted data via JSON blob as follows - -```javascript -{ - "data": "base64_encrypted_blob" -} -``` - -**Encrypting data via cURL** - -```bash -encryptURL=$(aws cloudformation describe-stacks --stack-name encryption-proxy --query 'Stacks[].Outputs[?OutputKey==`EncryptURL`].OutputValue' --output text) - -curl -H 'Content-Type: application/json' -X POST -d 'Here it is my super secret data...' ${encryptURL} -``` - -#### Decrypt Action - -* Consumes JSON blob received out of Encryption Action -* Returns decrypted data via JSON blob as follows - -```javascript -{ - "data": "decrypted_data" -} -``` - -**Decrypting data via cURL** - -```bash -decryptURL=$(aws cloudformation describe-stacks --stack-name encryption-proxy --query 'Stacks[].Outputs[?OutputKey==`DecryptURL`].OutputValue' --output text) - -curl -H 'Content-Type: application/json' -X POST -d '{ "data": "AQECAHjgf/RDFTAO0+ief3VXaJRnnKbyaaEsVhkmYxTcbiNhcgAAAHIwcAYJKoZIhvcNAQcGoGMw\nYQIBADBcBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDBX7iv8CwG1gr2Hc4wIBEIAvS1cXraMW\n3PU96z4AACGj7Wuo007HwWjtK/quSi3FKyYFvkJ10YhDOEvzxOD7Ntw=\n" }' ${decryptURL} -``` diff --git a/examples/2016-10-31/encryption_proxy/src/decryption.py b/examples/2016-10-31/encryption_proxy/src/decryption.py deleted file mode 100644 index ac04a86fa..000000000 --- a/examples/2016-10-31/encryption_proxy/src/decryption.py +++ /dev/null @@ -1,49 +0,0 @@ -import boto3 -import json -import base64 - -kms = boto3.client('kms') -bad_request = { - 'statusCode': 400, - 'headers': { - 'Content-Type': 'application/json' - }, - 'body': json.dumps({'error': 'Invalid argument'}) -} - - -def decrypt(message): - '''decrypt leverages KMS decrypt and base64-encode decrypted blob - - More info on KMS decrypt API: - https://docs.aws.amazon.com/kms/latest/APIReference/API_decrypt.html - ''' - try: - ret = kms.decrypt( - CiphertextBlob=base64.decodestring(message)) - decrypted_data = ret.get('Plaintext') - except Exception as e: - # returns http 500 back to user and log error details in Cloudwatch Logs - raise Exception("Unable to decrypt data: ", e) - - return decrypted_data - - -def post(event, context): - - try: - payload = json.loads(event['body']) - message = payload['data'] - except (KeyError, TypeError, ValueError): - return bad_request - - decrypted_message = decrypt(message) - response = {'data': decrypted_message} - - return { - 'statusCode': 200, - 'headers': { - 'Content-Type': 'application/json' - }, - 'body': json.dumps(response) - } diff --git a/examples/2016-10-31/encryption_proxy/src/encryption.py b/examples/2016-10-31/encryption_proxy/src/encryption.py deleted file mode 100644 index 6b9ecb4ea..000000000 --- a/examples/2016-10-31/encryption_proxy/src/encryption.py +++ /dev/null @@ -1,51 +0,0 @@ -import boto3 -import json -import base64 -import os - -kms = boto3.client('kms') -bad_request = { - 'statusCode': 400, - 'headers': { - 'Content-Type': 'application/json' - }, - 'body': json.dumps({'error': 'Invalid argument'}) -} - - -def encrypt(key, message): - '''encrypt leverages KMS encrypt and base64-encode encrypted blob - - More info on KMS encrypt API: - https://docs.aws.amazon.com/kms/latest/APIReference/API_encrypt.html - ''' - try: - ret = kms.encrypt(KeyId=key, Plaintext=message) - encrypted_data = base64.encodestring(ret.get('CiphertextBlob')) - except Exception as e: - # returns http 500 back to user and log error details in Cloudwatch Logs - raise Exception("Unable to encrypt data: ", e) - - return encrypted_data.decode() - - -def post(event, context): - - key_id = os.environ.get('keyId') - if key_id is None: - raise Exception("KMS Key ID not found.") - - message = event.get('body') - if message is None: - return bad_request - - encrypted_message = encrypt(key_id, message) - response = {'data': encrypted_message} - - return { - 'statusCode': 200, - 'headers': { - 'Content-Type': 'application/json' - }, - 'body': json.dumps(response) - } diff --git a/examples/2016-10-31/encryption_proxy/template.yaml b/examples/2016-10-31/encryption_proxy/template.yaml deleted file mode 100644 index 8ce67f466..000000000 --- a/examples/2016-10-31/encryption_proxy/template.yaml +++ /dev/null @@ -1,106 +0,0 @@ -AWSTemplateFormatVersion: '2010-09-09' -Transform: 'AWS::Serverless-2016-10-31' -Description: > - - This SAM example creates the following resources: - - Encryption Proxy: Comprised of Encryption and Decryption Lambda functions + IAM Roles - KMS Key: KMS Key and encrypt/decrypt permission for each IAM Role separately - - Last Modified: March 22nd November 2018 - Author: Heitor Lessa - -Outputs: - - EncryptURL: - Description: "Encrypt endpoint URL for Stage environment" - Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Stage/encrypt" - - DecryptURL: - Description: "Decrypt endpoint URL for Stage environment" - Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Stage/decrypt" - - EncryptFunction: - Description: "Encryption Lambda Function ARN" - Value: !GetAtt EncryptionFunction.Arn - - DecryptFunction: - Description: "Decryption Lambda Function ARN" - Value: !GetAtt DecryptionFunction.Arn - -Resources: - - - EncryptionDecryptionKey: - Type: AWS::KMS::Key - Properties: - Description: "Encryption and Decryption key for Lambda" - KeyPolicy: - Id: "LambdaEncDec" - Version: "2012-10-17" - Statement: - - - Sid: "Allow administration of the key" - Effect: "Allow" - Action: - - "kms:Create*" - - "kms:Describe*" - - "kms:Enable*" - - "kms:List*" - - "kms:Put*" - - "kms:Update*" - - "kms:Revoke*" - - "kms:Disable*" - - "kms:Get*" - - "kms:Delete*" - - "kms:ScheduleKeyDeletion" - - "kms:CancelKeyDeletion" - Principal: - AWS: !Ref AWS::AccountId - Resource: '*' - - - Sid: "Allow Encryption Service to use this key" - Effect: "Allow" - Action: - - "kms:Encrypt" - Principal: - AWS: !GetAtt EncryptionFunctionRole.Arn - Resource: '*' - - - Sid: "Allow Decryption Service to use this key" - Effect: "Allow" - Action: - - "kms:Decrypt" - Principal: - AWS: !GetAtt DecryptionFunctionRole.Arn - Resource: '*' - - - EncryptionFunction: - Type: 'AWS::Serverless::Function' - Properties: - Handler: encryption.post - Runtime: python2.7 - CodeUri: src/ - Events: - encryptAction: - Type: Api - Properties: - Path: /encrypt - Method: post - Environment: - Variables: - keyId: !Ref EncryptionDecryptionKey - - DecryptionFunction: - Type: 'AWS::Serverless::Function' - Properties: - Handler: decryption.post - Runtime: python2.7 - CodeUri: src/ - Events: - decryptAction: - Type: Api - Properties: - Path: /decrypt - Method: post diff --git a/examples/2016-10-31/eventbridge-event-to-msteams/events/event_federated.json b/examples/2016-10-31/eventbridge-event-to-msteams/events/event_federated.json deleted file mode 100644 index 6e7f1731d..000000000 --- a/examples/2016-10-31/eventbridge-event-to-msteams/events/event_federated.json +++ /dev/null @@ -1,68 +0,0 @@ -{ - "version": "0", - "id": "xxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", - "detail-type": "AWS API Call via CloudTrail", - "source": "aws.ec2", - "account": "123456789012", - "time": "2018-07-22T09:49:51Z", - "region": "ap-southeast-2", - "resources": [], - "detail": { - "eventVersion": "1.05", - "userIdentity": { - "type": "AssumedRole", - "principalId": "xxxxxxxxxxxxxxx:john.doh@example.com", - "arn": "arn:aws:sts::xxxxxxxxxxxxxxx:assumed-role/xxxxxxxxxxxxxxx/john.doh@example.com", - "accountId": "xxxxxxxxxxxxxxx", - "accessKeyId": "xxxxxxxxxxxxxxx", - "sessionContext": { - "attributes": { - "mfaAuthenticated": "false", - "creationDate": "2018-07-22T09:49:51Z" - }, - "sessionIssuer": { - "type": "Role", - "principalId": "xxxxxxxxxxxxxxx", - "arn": "arn:aws:iam::123456789012:role/xxxxxxxxxxxxxxx", - "accountId": "123456789012", - "userName": "xxxxxxxxxxxxxxx" - } - } - }, - "eventTime": "2018-07-22T09:49:51Z", - "eventSource": "ec2.amazonaws.com", - "eventName": "AuthorizeSecurityGroupIngress", - "awsRegion": "ap-southeast-2", - "sourceIPAddress": "xxxxxxxxxxxxxxx", - "userAgent": "console.ec2.amazonaws.com", - "requestParameters": { - "groupId": "sg-xxxxxxxxxxxxxx", - "ipPermissions": { - "items": [ - { - "ipProtocol": "tcp", - "fromPort": 22, - "toPort": 22, - "groups": {}, - "ipRanges": { - "items": [ - { - "cidrIp": "0.0.0.0/0" - } - ] - }, - "ipv6Ranges": {}, - "prefixListIds": {} - } - ] - } - }, - "responseElements": { - "_return": true - }, - "requestID": "xxxxxxxxxxxxxxx", - "eventID": "xxxxxxxxxxxxxxx", - "eventType": "AwsApiCall", - "recipientAccountId": "xxxxxxxxxxxxxxx" - } -} \ No newline at end of file diff --git a/examples/2016-10-31/eventbridge-event-to-msteams/events/event_iam.json b/examples/2016-10-31/eventbridge-event-to-msteams/events/event_iam.json deleted file mode 100644 index 59faaece0..000000000 --- a/examples/2016-10-31/eventbridge-event-to-msteams/events/event_iam.json +++ /dev/null @@ -1,64 +0,0 @@ -{ - "version": "0", - "id": "xxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", - "detail-type": "AWS API Call via CloudTrail", - "source": "aws.ec2", - "account": "123456789012", - "time": "2018-07-22T09:49:51Z", - "region": "ap-southeast-2", - "resources": [], - "detail": { - "eventVersion": "1.05", - "userIdentity": { - "type": "IAMUser", - "principalId": "XXXXXXXXXXXXXXXXXXX", - "arn": "arn:aws:iam::123456789012:user/john.doh", - "accountId": "123456789012", - "accessKeyId": "XXXXXXXXXXXXXXXXXXX", - "userName": "john.doh", - "sessionContext": { - "attributes": { - "mfaAuthenticated": "false", - "creationDate": "2018-07-22T02:00:21Z" - } - }, - "invokedBy": "signin.amazonaws.com" - }, - "eventTime": "2018-07-22T09:49:51Z", - "eventSource": "ec2.amazonaws.com", - "eventName": "AuthorizeSecurityGroupIngress", - "awsRegion": "ap-southeast-2", - "sourceIPAddress": "192.168.1.1", - "userAgent": "signin.amazonaws.com", - "requestParameters": { - "groupId": "sg-xxxxxxxxxxxxxxx", - "ipPermissions": { - "items": [ - { - "ipProtocol": "tcp", - "fromPort": 22, - "toPort": 22, - "groups": {}, - "ipRanges": { - "items": [ - { - "cidrIp": "0.0.0.0/0", - "description": "to lazy to put correct ip, lets give everyone access" - } - ] - }, - "ipv6Ranges": {}, - "prefixListIds": {} - } - ] - } - }, - "responseElements": { - "requestId": "xxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", - "_return": true - }, - "requestID": "xxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", - "eventID": "xxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", - "eventType": "AwsApiCall" - } -} \ No newline at end of file diff --git a/examples/2016-10-31/eventbridge-event-to-msteams/events/event_security_group.json b/examples/2016-10-31/eventbridge-event-to-msteams/events/event_security_group.json deleted file mode 100644 index 1ea839419..000000000 --- a/examples/2016-10-31/eventbridge-event-to-msteams/events/event_security_group.json +++ /dev/null @@ -1,64 +0,0 @@ -{ - "version": "0", - "id": "xxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", - "detail-type": "AWS API Call via CloudTrail", - "source": "aws.ec2", - "account": "123456789012", - "time": "2018-07-28T09:17:52Z", - "region": "ap-southeast-2", - "resources": [], - "detail": { - "eventVersion": "1.05", - "userIdentity": { - "type": "IAMUser", - "principalId": "XXXXXXXXXXXXXXXXXXX", - "arn": "arn:aws:iam::123456789012:user/john.doh", - "accountId": "123456789012", - "accessKeyId": "XXXXXXXXXXXXXXXXXXX", - "userName": "john.doh", - "sessionContext": { - "attributes": { - "mfaAuthenticated": "false", - "creationDate": "2018-07-28T02:20:35Z" - } - }, - "invokedBy": "signin.amazonaws.com" - }, - "eventTime": "2018-07-28T09:17:52Z", - "eventSource": "ec2.amazonaws.com", - "eventName": "AuthorizeSecurityGroupIngress", - "awsRegion": "ap-southeast-2", - "sourceIPAddress": "192.168.1.10", - "userAgent": "signin.amazonaws.com", - "requestParameters": { - "groupId": "sg-xxxxxxxxxxxxxxx", - "ipPermissions": { - "items": [ - { - "ipProtocol": "tcp", - "fromPort": 3389, - "toPort": 3389, - "groups": { - "items": [ - { - "groupId": "sg-zzzzzzzzzzzzzzz", - "description": "All RDP from front-end security group" - } - ] - }, - "ipRanges": {}, - "ipv6Ranges": {}, - "prefixListIds": {} - } - ] - } - }, - "responseElements": { - "requestId": "xxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", - "_return": true - }, - "requestID": "xxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", - "eventID": "xxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", - "eventType": "AwsApiCall" - } -} \ No newline at end of file diff --git a/examples/2016-10-31/eventbridge-event-to-msteams/template.yaml b/examples/2016-10-31/eventbridge-event-to-msteams/template.yaml deleted file mode 100644 index cbffa06d5..000000000 --- a/examples/2016-10-31/eventbridge-event-to-msteams/template.yaml +++ /dev/null @@ -1,63 +0,0 @@ -# To deploy with SAM: -# download and install sam: https://github.com/awslabs/aws-sam-cli -# sam package --template-file template.yaml --s3-bucket your-s3-bucket --output-template sam-output.yaml -# sam deploy --template-file sam-output.yaml --stack-name WatchSecurityGroup --capabilities CAPABILITY_IAM - -# To test locally: (make sure to modifify the "groupId" with a real security group, add your webhook into vars.json) - # sam local invoke -e events/event_iam.json WatchSecurityGroupFunction -n vars.json - -Transform: AWS::Serverless-2016-10-31 -Description: Lambda function to watch EC2 Security Group Events to Send to Teams - -Parameters: - - WebHook: - Description: MS Teams Webhook - Type: String - Default: https://outlook.office.com/webhook/your/webhook/etc - -Resources: - - # Lambda Function - WatchSecurityGroupFunction: - Type: AWS::Serverless::Function - Properties: - Handler: watch-security-group.lambda_handler - Timeout: 10 - Tracing: Active - MemorySize: 128 - Runtime: python3.6 - CodeUri: . - Description: Detects EC2 Security Group Events to Send to Teams - Policies: - - AWSLambdaVPCAccessExecutionRole - - CloudWatchLogsFullAccess - - Version: 2012-10-17 - Statement: - - Effect: Allow - Action: - - ec2:DescribeSecurityGroupReferences - - ec2:DescribeSecurityGroups - - ec2:DescribeStaleSecurityGroups - Resource: '*' - Events: - WatchSecurityGroupRule: - Type: EventBridgeRule - Description: Detects EC2 Security Group Events to Send to Teams - Properties: - EventBusName: event-bus-name - Pattern: - source: - - "aws.ec2" - detail-type: - - "AWS API Call via CloudTrail" - detail: - eventSource: - - "ec2.amazonaws.com" - eventName: - - "AuthorizeSecurityGroupIngress" - - "RevokeSecurityGroupIngress" - Environment: - Variables: - TEAM_WEBHOOK: !Ref WebHook - diff --git a/examples/2016-10-31/eventbridge-event-to-msteams/vars.json b/examples/2016-10-31/eventbridge-event-to-msteams/vars.json deleted file mode 100644 index f354f8109..000000000 --- a/examples/2016-10-31/eventbridge-event-to-msteams/vars.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "WatchSecurityGroupFunction": { - "TEAM_WEBHOOK": "https://outlook.office.com/webhook/your/webhook/etc" - } -} \ No newline at end of file diff --git a/examples/2016-10-31/eventbridge-event-to-msteams/watch-security-group.py b/examples/2016-10-31/eventbridge-event-to-msteams/watch-security-group.py deleted file mode 100644 index 245a3b73b..000000000 --- a/examples/2016-10-31/eventbridge-event-to-msteams/watch-security-group.py +++ /dev/null @@ -1,167 +0,0 @@ -# To test locally: (make sure to modifify the "groupId" with a real security group, add your webhook into vars.json) -# sam local invoke -e events/event_iam.json WatchSecurityGroupFunction -n vars.json - -import boto3 -import json -import os -from botocore.vendored import requests - -# Boto3 for security group: https://boto3.readthedocs.io/en/latest/reference/services/ec2.html#EC2.SecurityGroup -ec2 = boto3.resource('ec2') - -# Environment Variable for Lambda -TEAM_WEBHOOK = os.environ['TEAM_WEBHOOK'] - -def get_security_group(event): - - print("--------- Event Received ---------") - print(json.dumps(event)) - print("--------- End of Event ---------") - - # Read event from something change security groups in console. - if len(event["detail"]["requestParameters"]["ipPermissions"]) > 0: - for rule in event["detail"]["requestParameters"]["ipPermissions"]["items"]: - - # Use protocol as port if fromPort is empty - from_port = rule.get("fromPort", rule["ipProtocol"]) - - # Check for multi ip address added as source - if len(rule["ipRanges"]) > 0: - - for i in rule["ipRanges"]["items"]: - - # Check if CIDR has been entered or Another Security Group - try: - cidr_address = i["cidrIp"] - except KeyError: - cidr_address = i["groupId"] - - # Get description of individual rule - description = i.get("description", "") - - # Send Message - lookup_security_group(event, from_port, cidr_address, description) - - # Check for multi security groups added as source - if len(rule["groups"]) > 0: - - for i in rule["groups"]["items"]: - - # Check if CIDR has been entered or Another Security Group - try: - cidr_address = i["cidrIp"] - except KeyError: - cidr_address = i["groupId"] - - # Get description of individual rule - description = i.get("description", "") - - # Send Message - lookup_security_group(event, from_port, cidr_address, description) - - # Event isn't coming from Console, use API event - else: - - # Check if CIDR has been entered or Another Security Group - try: - cidr_address = event["detail"]["requestParameters"]["cidrIp"] - except KeyError: - cidr_address = event["detail"]["requestParameters"]["groupId"] - - # Get Description and Port - description = event["detail"]["requestParameters"].get("description", "") - from_port = event["detail"]["requestParameters"].get("from_port", -1) - - # Send Message - lookup_security_group(event, from_port, cidr_address, description) - - -def lookup_security_group(event, source_port, cidr_address, description): - - # Look up security group attributes from group_id - try: - lookup_sg_id = event["detail"]["requestParameters"]["groupId"] - lookup_sg = ec2.SecurityGroup(lookup_sg_id) - lookup_sg_name = lookup_sg.group_name - lookup_sg_description = lookup_sg.description - - # Get Account and User Details - event_type = event["detail"]["eventName"] - aws_account = event["account"] - user_id = event["detail"]["userIdentity"]["arn"] - full_username = user_id.split(':')[-1] - - # Colors for Microsoft Teams: Blue = Add, Red = Remove - if event_type == "AuthorizeSecurityGroupIngress": - message_color = "0072C6" - else: - message_color = "FF0000" - - # Send Teams Message - create_json_message(TEAM_WEBHOOK, full_username, lookup_sg_id, lookup_sg_name, lookup_sg_description, aws_account, source_port, cidr_address, description, event_type, message_color) - - except Exception as error: - print("Error getting Security Group from Event:") - print(f"{error}") - - -def create_json_message(TEAM_WEBHOOK, full_username, lookup_sg_id, lookup_sg_name, lookup_sg_description, aws_account, source_port, cidr_address, description, event_type, message_color): - - body = { - "@context": "http://schema.org/extensions", - "@type": "MessageCard", - "themeColor": f"{message_color}", - "title": "Security Group Watcher", - "text": " ", - "sections": [ - { - "activityText": f"Security group **{lookup_sg_id}** has been modified and trigger this alert" - }, - { - "facts": [ - { - "name": "Action", - "value": f"{event_type}" - }, - { - "name": "User", - "value": f"{full_username}" - }, - { - "name": "Account", - "value": f"{aws_account}" - }, - { - "name": "SourcePort", - "value": f"{source_port}" - }, - { - "name": "Destination", - "value": f"{cidr_address}" - }, - { - "name": "SecurityGroupId", - "value": f"{lookup_sg_id}" - }, - { - "name": "Description", - "value": f"{lookup_sg_description}" - }, - { - "name": "Rule Description", - "value": f"{description}" - } - - ] - } - ] - } - - response = requests.post(TEAM_WEBHOOK, data=json.dumps(body)) - print(f'response from Teams: {response}') - - return response - - -def lambda_handler(event, context): - get_security_group(event) diff --git a/examples/2016-10-31/function_lambda_event_destinations/README.md b/examples/2016-10-31/function_lambda_event_destinations/README.md deleted file mode 100644 index 0d1f6c9e6..000000000 --- a/examples/2016-10-31/function_lambda_event_destinations/README.md +++ /dev/null @@ -1,19 +0,0 @@ -# API Gateway + Lambda REQUEST Authorizer Example - -This example shows you how to configure Event Invoke Config on a function. - -## Running the example - -Replace the Destination Arns for SQS, SNS, EventBridge with valid arns. -Deploy the example into your account: - -```bash -$ sam deploy \ - --template-file /path_to_template/packaged-template.yaml \ - --stack-name my-new-stack \ - --capabilities CAPABILITY_IAM -``` - -## Additional resources - -- https://aws.amazon.com/blogs/compute/introducing-aws-lambda-destinations/ diff --git a/examples/2016-10-31/function_lambda_event_destinations/src/index.js b/examples/2016-10-31/function_lambda_event_destinations/src/index.js deleted file mode 100644 index 22c465464..000000000 --- a/examples/2016-10-31/function_lambda_event_destinations/src/index.js +++ /dev/null @@ -1,15 +0,0 @@ -exports.handler = function(event, context, callback) { - var event_received_at = new Date().toISOString(); - console.log('Event received at: ' + event_received_at); - console.log('Received event:', JSON.stringify(event, null, 2)); - - if (event.Success) { - console.log("Success"); - context.callbackWaitsForEmptyEventLoop = false; - callback(null); - } else { - console.log("Failure"); - context.callbackWaitsForEmptyEventLoop = false; - callback(new Error("Failure from event, Success = false, I am failing!"), 'Destination Function Error Thrown'); - } -}; diff --git a/examples/2016-10-31/function_lambda_event_destinations/template.yaml b/examples/2016-10-31/function_lambda_event_destinations/template.yaml deleted file mode 100644 index 8a2b57458..000000000 --- a/examples/2016-10-31/function_lambda_event_destinations/template.yaml +++ /dev/null @@ -1,83 +0,0 @@ -AWSTemplateFormatVersion: '2010-09-09' -Transform: AWS::Serverless-2016-10-31 -Description: Lambda Event Destinations -Parameters: - SQSArn: - Type: String - Default: my-sqs-arn - UseExistingQueue: - Type: String - AllowedValues: - - true - - false - Default: true - CreateSNSTopic: - Type: String - AllowedValues: - - true - - false - Default: true -Conditions: - QueueCreationDisabled: !Equals [!Ref UseExistingQueue, true] - TopicCreationEnabled: !Equals [!Ref CreateSNSTopic, true] - -Resources: - EventDestinationLambda: - Type: AWS::Serverless::Function - Properties: - EventInvokeConfig: - MaximumEventAgeInSeconds: 70 - MaximumRetryAttempts: 1 - DestinationConfig: - OnSuccess: - Type: Lambda - # If the type is Lambda/EventBridge, Destination property is required. - Destination: !GetAtt DestinationLambda.Arn - OnFailure: - # SQS and SNS will get auto-created if the Destination property is not specified or is AWS::NoValue - Type: SQS - CodeUri: ./src/ - Handler: index.handler - Runtime: nodejs12.x - MemorySize: 1024 - - EventDestinationSQSSNS: - Type: AWS::Serverless::Function - Properties: - EventInvokeConfig: - MaximumEventAgeInSeconds: 70 - MaximumRetryAttempts: 1 - DestinationConfig: - OnSuccess: - Type: SQS - Destination: !If [QueueCreationDisabled, !Ref SQSArn, !Ref 'AWS::NoValue'] - OnFailure: - Type: SNS - Destination: !If [TopicCreationEnabled, !Ref 'AWS::NoValue', 'SOME-SNS-ARN'] - CodeUri: ./src/ - Handler: index.handler - Runtime: nodejs12.x - MemorySize: 1024 - - DestinationLambda: - Type: AWS::Serverless::Function - Properties: - InlineCode: | - exports.handler = async (event) => { - const response = { - statusCode: 200, - body: JSON.stringify('Hello from Lambda!'), - }; - return response; - }; - Handler: index.handler - Runtime: nodejs12.x - MemorySize: 1024 - SNSSubscription: - Type: AWS::SNS::Subscription - Condition: TopicCreationEnabled - Properties: - Endpoint: example@example.com - Protocol: email - # Refer to the auto-created SNS topic using .DestinationTopic - TopicArn: !Ref EventDestinationSQSSNS.DestinationTopic diff --git a/examples/2016-10-31/function_request_parameters/src/index.js b/examples/2016-10-31/function_request_parameters/src/index.js deleted file mode 100644 index 7f7a6018d..000000000 --- a/examples/2016-10-31/function_request_parameters/src/index.js +++ /dev/null @@ -1,6 +0,0 @@ -exports.handler = function(event, context, callback) { - callback(null, { - "statusCode": 200, - "body": "hello world" - }); -} diff --git a/examples/2016-10-31/function_request_parameters/template.yaml b/examples/2016-10-31/function_request_parameters/template.yaml deleted file mode 100644 index 4900fb0c6..000000000 --- a/examples/2016-10-31/function_request_parameters/template.yaml +++ /dev/null @@ -1,33 +0,0 @@ -AWSTemplateFormatVersion: '2010-09-09' -Transform: AWS::Serverless-2016-10-31 -Description: Simple API Endpoint configured using Swagger specified inline and backed by a Lambda function -Globals: - Api: - CacheClusterEnabled: true - CacheClusterSize: '0.5' - -Resources: - - MyLambdaFunction: - Type: AWS::Serverless::Function - Properties: - Handler: index.handler - Runtime: nodejs12.x - CodeUri: src/ - Events: - GetApi: - Type: Api - Properties: - Path: /post - Method: POST - RequestParameters: - - method.request.header.Authorization: - Required: true - Caching: true - - method.request.querystring.type - -Outputs: - - ApiURL: - Description: "API endpoint URL for Prod environment" - Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/post" \ No newline at end of file diff --git a/examples/2016-10-31/hello-world-golang/main b/examples/2016-10-31/hello-world-golang/main deleted file mode 100755 index 9f2fce902..000000000 Binary files a/examples/2016-10-31/hello-world-golang/main and /dev/null differ diff --git a/examples/2016-10-31/hello-world-golang/main.go b/examples/2016-10-31/hello-world-golang/main.go deleted file mode 100644 index 3edcdab38..000000000 --- a/examples/2016-10-31/hello-world-golang/main.go +++ /dev/null @@ -1,29 +0,0 @@ -package main - -import ( - "encoding/json" - "log" - - "github.com/aws/aws-lambda-go/events" - "github.com/aws/aws-lambda-go/lambda" -) - -type body struct { - Message string `json:"message"` -} - -// Handler is the Lambda function handler -func Handler(request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { - log.Println("Lambda request", request.RequestContext.RequestID) - - b, _ := json.Marshal(body{Message: "Hello World!"}) - - return events.APIGatewayProxyResponse{ - Body: string(b), - StatusCode: 200, - }, nil -} - -func main() { - lambda.Start(Handler) -} \ No newline at end of file diff --git a/examples/2016-10-31/hello-world-golang/readme b/examples/2016-10-31/hello-world-golang/readme deleted file mode 100644 index 6fd829033..000000000 --- a/examples/2016-10-31/hello-world-golang/readme +++ /dev/null @@ -1,3 +0,0 @@ -Steps to run sample app. - - GOOS=linux go build -o main - - sam local start-api \ No newline at end of file diff --git a/examples/2016-10-31/hello-world-golang/template.yaml b/examples/2016-10-31/hello-world-golang/template.yaml deleted file mode 100644 index f7c29807e..000000000 --- a/examples/2016-10-31/hello-world-golang/template.yaml +++ /dev/null @@ -1,19 +0,0 @@ -AWSTemplateFormatVersion: '2010-09-09' -Transform: 'AWS::Serverless-2016-10-31' -Description: A starter AWS Lambda function. -Resources: - helloworld: - Type: 'AWS::Serverless::Function' - Properties: - Handler: main - Runtime: go1.x - CodeUri: . - Description: A starter AWS Lambda function. - MemorySize: 128 - Timeout: 3 - Events: - HelloWorld: - Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api - Properties: - Path: /hello - Method: get \ No newline at end of file diff --git a/examples/2016-10-31/hello_world/src/index.js b/examples/2016-10-31/hello_world/src/index.js deleted file mode 100644 index 300dd0e6d..000000000 --- a/examples/2016-10-31/hello_world/src/index.js +++ /dev/null @@ -1,6 +0,0 @@ -'use strict'; -console.log('Loading function'); - -exports.handler = (event, context, callback) => { - callback(null, 'Hello World!'); -}; \ No newline at end of file diff --git a/examples/2016-10-31/hello_world/template.yaml b/examples/2016-10-31/hello_world/template.yaml deleted file mode 100644 index 1aef9bd76..000000000 --- a/examples/2016-10-31/hello_world/template.yaml +++ /dev/null @@ -1,11 +0,0 @@ -AWSTemplateFormatVersion : '2010-09-09' -Transform: AWS::Serverless-2016-10-31 -Description: A hello world application. - -Resources: - HelloWorldFunction: - Type: AWS::Serverless::Function - Properties: - Handler: index.handler - Runtime: nodejs12.x - CodeUri: src/ \ No newline at end of file diff --git a/examples/2016-10-31/hello_world_vpc/README.md b/examples/2016-10-31/hello_world_vpc/README.md deleted file mode 100644 index 5d2c473c1..000000000 --- a/examples/2016-10-31/hello_world_vpc/README.md +++ /dev/null @@ -1,55 +0,0 @@ - -# Lambda function with VPC Access - -This example shows you how to create a Lambda function in a VPC with the appropriate permissions using SAM. It primarily aims to demonstrate Cloudformation parameters as well as a simplified configuration made possible with SAM Policies, therefore it'll not utilise API Gateway or any other Event source and as a result only the account owner can invoke it. - -It is important to remember that VPC-enabled functions need NAT in order to access any public IP address (if needed) and therefore should be in a private subnet with VPC NAT Gateway and not VPC Internet Gateway. - -## Running the example - -Deploy the example into your account: - -```bash -# Replace YOUR_S3_ARTIFACTS_BUCKET -aws cloudformation package --template-file template.yaml --output-template-file template.packaged.yaml --s3-bucket YOUR_S3_ARTIFACTS_BUCKET - -# Replace YOUR_SECURITY_GROUP_1 and YOUR_SECURITY_GROUP_2 -# Replace YOUR_SUBNET_1 and YOUR_SUBNET_2 -## They must belong to the same VPC -aws cloudformation deploy \ - --template-file template.packaged.yaml \ - --stack-name sam-example-hello-vpc \ - --capabilities CAPABILITY_IAM \ - --parameter-overrides SecurityGroupIds="YOUR_SECURITY_GROUP_1,YOUR_SECURITY_GROUP_2" VpcSubnetIds="YOUR_SUBNET_1,YOUR_SUBNET_2" -``` - -Invoke the Lambda function using Lambda Invoke API via AWS CLI: - -```bash -hello_function_vpc=$(aws cloudformation describe-stacks \ - --stack-name hello-vpc-sample \ - --query 'Stacks[].Outputs[?OutputKey==`HelloWorldFunction`].OutputValue' \ - --output text) - -aws lambda invoke --function-name $hello_function_vpc result.txt -``` - -If successful, you should see a similar output and the function return under ``result.txt``: - -```javascript -{ - "StatusCode": 200, - "ExecutedVersion": "$LATEST" -} -``` -```bash -cat result.txt - -"Hello world!" -``` - - -## Additional resources - -- https://docs.aws.amazon.com/lambda/latest/dg/vpc.html -- https://aws.amazon.com/premiumsupport/knowledge-center/nat-gateway-vpc-private-subnet/ diff --git a/examples/2016-10-31/hello_world_vpc/src/index.js b/examples/2016-10-31/hello_world_vpc/src/index.js deleted file mode 100644 index 34d056da6..000000000 --- a/examples/2016-10-31/hello_world_vpc/src/index.js +++ /dev/null @@ -1 +0,0 @@ -exports.handler = async () => 'Hello world!' \ No newline at end of file diff --git a/examples/2016-10-31/hello_world_vpc/template.yaml b/examples/2016-10-31/hello_world_vpc/template.yaml deleted file mode 100644 index 73c76ad11..000000000 --- a/examples/2016-10-31/hello_world_vpc/template.yaml +++ /dev/null @@ -1,34 +0,0 @@ -AWSTemplateFormatVersion : '2010-09-09' -Transform: AWS::Serverless-2016-10-31 -Description: A hello world function with VPC Access. - - -Parameters: - SecurityGroupIds: - Type: List - Description: Security Group IDs that Lambda will use - VpcSubnetIds: - Type: List - Description: VPC Subnet IDs that Lambda will use (min 2 for HA) - - -Resources: - HelloWorldFunction: - Type: AWS::Serverless::Function - Properties: - Handler: index.handler - Runtime: nodejs12.x - CodeUri: src/ - Policies: - - VPCAccessPolicy: {} - # This policy gives permission for Lambdas to create/manage ENIs - # SAM Policy templates you can use: https://github.com/awslabs/serverless-application-model/blob/develop/examples/2016-10-31/policy_templates/all_policy_templates.yaml - VpcConfig: - SecurityGroupIds: !Ref SecurityGroupIds - SubnetIds: !Ref VpcSubnetIds - -Outputs: - - HelloWorldFunction: - Description: "Hello World Lambda Function ARN" - Value: !GetAtt HelloWorldFunction.Arn diff --git a/examples/2016-10-31/http_api_cors/README.md b/examples/2016-10-31/http_api_cors/README.md deleted file mode 100644 index cb48bca40..000000000 --- a/examples/2016-10-31/http_api_cors/README.md +++ /dev/null @@ -1,12 +0,0 @@ -# HttpApi with CORS Example - -Example SAM template to configure CORS for HttpApi - -## Running the example - -```bash -$ sam deploy \ - --template-file template.yaml \ - --stack-name my-stack-name \ - --capabilities CAPABILITY_IAM -``` diff --git a/examples/2016-10-31/http_api_cors/template.yaml b/examples/2016-10-31/http_api_cors/template.yaml deleted file mode 100644 index 9654dfc9d..000000000 --- a/examples/2016-10-31/http_api_cors/template.yaml +++ /dev/null @@ -1,48 +0,0 @@ -AWSTemplateFormatVersion : '2010-09-09' -Transform: AWS::Serverless-2016-10-31 -Description: Http Api with Cors. - -Resources: - HttpApiFunction: - Type: AWS::Serverless::Function - Properties: - InlineCode: | - exports.handler = async (event) => { - console.log("Hello from MyAuthFunction") - return { - statusCode: 200, - body: JSON.stringify(event), - headers: {} - } - } - Handler: index.handler - Runtime: nodejs12.x - Events: - SimpleCase: - Type: HttpApi - Properties: - ApiId: !Ref MyApi - - MyApi: - Type: AWS::Serverless::HttpApi - Properties: - FailOnWarnings: true - CorsConfiguration: - AllowHeaders: - - x-apigateway-header - AllowMethods: - - GET - AllowOrigins: - - https://foo.com - ExposeHeaders: - - x-amzn-header - DefinitionBody: - info: - version: '1.0' - title: - Ref: AWS::StackName - paths: - "$default": - x-amazon-apigateway-any-method: - isDefaultRoute: true - openapi: 3.0.1 \ No newline at end of file diff --git a/examples/2016-10-31/image_resize_golang/README.md b/examples/2016-10-31/image_resize_golang/README.md deleted file mode 100644 index 60b72424d..000000000 --- a/examples/2016-10-31/image_resize_golang/README.md +++ /dev/null @@ -1,27 +0,0 @@ -# AWS::Serverless::S3 Event Code Example -This example shows you how to get events of S3 bucket. When you upload an image in the source bucket it will resize that image and save in the destination bucket. -###### Note -Don't forget to chnage S3 bucket name, source bucket prefix. - -## Package and deploy template -#### Build -Run the following command to build binary of your code -``` -GOOS=linux go build -o main -``` -#### Package -To package your application run the following command -``` -sam package --template-file template.yaml --output-template-file serverless-output.yaml --s3-bucket -``` -It will validate the template, zip you applicaiton, upload to your S3 bucket and generates the output template. Replace the `` with your bucket name. -#### Deploy -Run the following command replacing `` with your desire prefix name to deploye your application -``` -sam deploy --template-file serverless-output.yaml --stack-name image-resizer --capabilities CAPABILITY_IAM --parameter-overrides BucketNamePrefix= -``` -It will create necessary resources and link them according to the template using cloudformation. - -## Test -Upload an image in `JPEG` format to the `SourceBucket` defined in the template and verify the same image with smaller size in `DestBucket`. - diff --git a/examples/2016-10-31/image_resize_golang/main.go b/examples/2016-10-31/image_resize_golang/main.go deleted file mode 100644 index 646d2bc74..000000000 --- a/examples/2016-10-31/image_resize_golang/main.go +++ /dev/null @@ -1,72 +0,0 @@ -package main - -import ( - "bytes" - "github.com/aws/aws-lambda-go/events" - "github.com/aws/aws-lambda-go/lambda" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/session" - "github.com/aws/aws-sdk-go/service/s3" - "github.com/aws/aws-sdk-go/service/s3/s3manager" - "github.com/nfnt/resize" - "image" - "image/jpeg" - "log" - "os" -) - -func Handler(event events.S3Event) (string, error) { - dstBucket := os.Getenv("DestBucket") - svc := s3.New(session.New()) - downloader := s3manager.NewDownloaderWithClient(svc) - uploader := s3manager.NewUploaderWithClient(svc) - - for _, record := range event.Records { - srcBucket := record.S3.Bucket.Name - srcKey := record.S3.Object.Key - imgSize := record.S3.Object.Size - - file := make([]byte, imgSize) - _, err := downloader.Download(aws.NewWriteAtBuffer(file), - &s3.GetObjectInput{ - Bucket: aws.String(srcBucket), - Key: aws.String(srcKey), - }) - if err != nil { - log.Fatalln("Donwload error: " + err.Error()) - } - - reader := bytes.NewReader(file) - img, _, err := image.Decode(reader) - if err != nil { - log.Fatalln("Decode error: " + err.Error()) - } - - resizedImg := resize.Resize(300, 0, img, resize.Lanczos3) - - buf := new(bytes.Buffer) - err = jpeg.Encode(buf, resizedImg, &jpeg.Options{ - Quality: 50, - }) - if err != nil { - log.Fatalln("Encode error: " + err.Error()) - } - - imgBody := bytes.NewReader(buf.Bytes()) - - _, err = uploader.Upload(&s3manager.UploadInput{ - Bucket: aws.String(dstBucket), - Key: aws.String(srcKey), - Body: imgBody, - }) - if err != nil { - log.Fatalln("Upload error: " + err.Error()) - } - } - - return "Resize successful", nil -} - -func main() { - lambda.Start(Handler) -} diff --git a/examples/2016-10-31/image_resize_golang/template.yaml b/examples/2016-10-31/image_resize_golang/template.yaml deleted file mode 100644 index e8f99ece8..000000000 --- a/examples/2016-10-31/image_resize_golang/template.yaml +++ /dev/null @@ -1,54 +0,0 @@ -AWSTemplateFormatVersion: '2010-09-09' -Transform: AWS::Serverless-2016-10-31 -Description: A function is triggered off an upload to a bucket. It uploads a resized image to another bucket. - -Globals: - Function: - Timeout: 20 - -Parameters: - BucketNamePrefix: - Type: String - Default: sam-example - -Resources: - ImageProcessorFunction: - Type: AWS::Serverless::Function - Properties: - CodeUri: . - Handler: main - Runtime: go1.x - Tracing: Active - Policies: - - S3ReadPolicy: - BucketName: !Sub "${BucketNamePrefix}-source-bucket" - - S3CrudPolicy: - BucketName: !Sub "${BucketNamePrefix}-dest-bucket" - Environment: - Variables: - DestBucket: !Sub "${BucketNamePrefix}-dest-bucket" - Events: - ImageUpload: - Type: S3 - Properties: - Bucket: !Ref SourceBucket - Events: s3:ObjectCreated:* - - SourceBucket: - Type: AWS::S3::Bucket - Properties: - BucketName: !Sub "${BucketNamePrefix}-source-bucket" - - DestBucket: - Type: AWS::S3::Bucket - Properties: - BucketName: !Sub "${BucketNamePrefix}-dest-bucket" - -Outputs: - SourceBucket: - Description: "S3 Bucket name that will trigger a Lambda function upon new objects insertion" - Value: !Ref SourceBucket - DestBucket: - Description: "S3 Bucket name that will store a resized image" - Value: !Ref DestBucket - diff --git a/examples/2016-10-31/image_resize_python/README.md b/examples/2016-10-31/image_resize_python/README.md deleted file mode 100644 index f7cb7990d..000000000 --- a/examples/2016-10-31/image_resize_python/README.md +++ /dev/null @@ -1,44 +0,0 @@ -# AWS::Serverless::LayerVersion Library Code Example - -This example shows you how to download and publish an application that bundles its Function dependencies into an AWS::Serverless::LayerVersion resource. The function resizes an image that is uploaded to the source bucket and saves the resized image to the destination bucket. - -## Running the example - -Below are instructions on how to deploy and use this example. - -### Download dependencies: - -We are using the [`sam build`](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-cli-command-reference-sam-build.html) command to download the correct versions of all of our python dependencies. At the time of creating this example, `sam build` does not support building layers, but that may change in the future. We can still use it to get our dependencies and then separate them out into separate directories for our Lamda Function and LayerVersion. -``` -# Use `sam build` to get dependencies -sam build -b ./build --use-container -m ./requirements.txt - -# Move dependencies into our layer directory -mv ./build/ImageProcessorFunction/PIL ./layer/ -mv ./build/ImageProcessorFunction/Pillow* ./layer/ - -# Remove the build directory and its contents -rm -rf ./build -``` - -### Package and deploy template: - -**Package:** -``` -# Run the following command, replacing s3_bucket with an existing bucket name -aws cloudformation package --template-file template.yaml --s3-bucket --output-template-file packaged.yaml -``` - - -**Deploy:** -``` -# Run the following command, replacing bucket_prefix with the desired prefix name for the s3 buckets this example will create -aws cloudformation deploy --template-file ./packaged.yaml --stack-name layers-example-stack --capabilities CAPABILITY_IAM --parameter-overrides BucketNamePrefix= -``` - -### Test example: - -Upload a picture to the `SourceBucket` defined in the template and verify that it creates a smaller version of the same picture in the `DestBucket` created in the same stack. - -Code taken from https://docs.aws.amazon.com/lambda/latest/dg/with-s3-example-deployment-pkg.html#with-s3-example-deployment-pkg-python - diff --git a/examples/2016-10-31/image_resize_python/layer/__init__.py b/examples/2016-10-31/image_resize_python/layer/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/examples/2016-10-31/image_resize_python/requirements.txt b/examples/2016-10-31/image_resize_python/requirements.txt deleted file mode 100644 index 3d03442e8..000000000 --- a/examples/2016-10-31/image_resize_python/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -pillow==6.2.0 \ No newline at end of file diff --git a/examples/2016-10-31/image_resize_python/src/__init__.py b/examples/2016-10-31/image_resize_python/src/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/examples/2016-10-31/image_resize_python/src/index.py b/examples/2016-10-31/image_resize_python/src/index.py deleted file mode 100644 index cc4f278ba..000000000 --- a/examples/2016-10-31/image_resize_python/src/index.py +++ /dev/null @@ -1,28 +0,0 @@ -import boto3 -import logging -import os -import uuid - -# Add to path so that Layers can be imported -import sys -sys.path.insert(0, '/opt') -from PIL import Image - -s3_client = boto3.client('s3') -dest_bucket = os.environ['DestBucket'] - -def resize_image(image_path, resized_path): - with Image.open(image_path) as image: - image.thumbnail(tuple(x / 2 for x in image.size)) - image.save(resized_path) - -def resize(event, context): - logging.info(event) - for record in event['Records']: - bucket = record['s3']['bucket']['name'] - key = record['s3']['object']['key'] - download_path = '/tmp/{}{}'.format(uuid.uuid4(), key) - upload_path = '/tmp/resized-{}'.format(key) - s3_client.download_file(bucket, key, download_path) - resize_image(download_path, upload_path) - s3_client.upload_file(upload_path, dest_bucket, 'resized-{}'.format(key)) diff --git a/examples/2016-10-31/image_resize_python/template.yaml b/examples/2016-10-31/image_resize_python/template.yaml deleted file mode 100644 index 9d304d149..000000000 --- a/examples/2016-10-31/image_resize_python/template.yaml +++ /dev/null @@ -1,56 +0,0 @@ -AWSTemplateFormatVersion: '2010-09-09' -Transform: AWS::Serverless-2016-10-31 -Description: A function is triggered off an upload to a bucket. It uploads a resized image to another bucket. -Parameters: - BucketNamePrefix: - Type: String - Default: sam-example -Resources: - ImageProcessorFunction: - Type: AWS::Serverless::Function - Properties: - Handler: index.resize - Runtime: python3.6 - CodeUri: ./src - Policies: - - S3ReadPolicy: - BucketName: !Sub "${BucketNamePrefix}-source-bucket" - - S3CrudPolicy: - BucketName: !Sub "${BucketNamePrefix}-dest-bucket" - Environment: - Variables: - DestBucket: !Sub "${BucketNamePrefix}-dest-bucket" - Events: - ImageUpload: - Type: S3 - Properties: - Bucket: !Ref SourceBucket - Events: s3:ObjectCreated:* - Layers: - - !Ref PillowLibrary - Tracing: Active - - PillowLibrary: - Type: 'AWS::Serverless::LayerVersion' - Properties: - ContentUri: ./layer - CompatibleRuntimes: # optional - - python3.6 - - SourceBucket: - Type: AWS::S3::Bucket - Properties: - BucketName: !Sub "${BucketNamePrefix}-source-bucket" - - DestBucket: - Type: AWS::S3::Bucket - Properties: - BucketName: !Sub "${BucketNamePrefix}-dest-bucket" - -Outputs: - SourceBucket: - Description: "S3 Bucket name that will trigger a Lambda function upon new objects insertion" - Value: !Ref SourceBucket - DestBucket: - Description: "S3 Bucket name that will store the resized versions of the images from the source bucket" - Value: !Ref DestBucket diff --git a/examples/2016-10-31/implicit_api_settings/src/index.js b/examples/2016-10-31/implicit_api_settings/src/index.js deleted file mode 100644 index 82ceee85a..000000000 --- a/examples/2016-10-31/implicit_api_settings/src/index.js +++ /dev/null @@ -1,22 +0,0 @@ -exports.handler = function(event, context, callback) { - - gifImageBase64 = "R0lGODlhPQBEAPeoAJosM//AwO/AwHVYZ/z595kzAP/s7P+goOXMv8+fhw/v739/f+8PD98fH/8mJl+fn/9ZWb8/PzWlwv///6wWGbImAPgTEMImIN9gUFCEm/gDALULDN8PAD6atYdCTX9gUNKlj8wZAKUsAOzZz+UMAOsJAP/Z2ccMDA8PD/95eX5NWvsJCOVNQPtfX/8zM8+QePLl38MGBr8JCP+zs9myn/8GBqwpAP/GxgwJCPny78lzYLgjAJ8vAP9fX/+MjMUcAN8zM/9wcM8ZGcATEL+QePdZWf/29uc/P9cmJu9MTDImIN+/r7+/vz8/P8VNQGNugV8AAF9fX8swMNgTAFlDOICAgPNSUnNWSMQ5MBAQEJE3QPIGAM9AQMqGcG9vb6MhJsEdGM8vLx8fH98AANIWAMuQeL8fABkTEPPQ0OM5OSYdGFl5jo+Pj/+pqcsTE78wMFNGQLYmID4dGPvd3UBAQJmTkP+8vH9QUK+vr8ZWSHpzcJMmILdwcLOGcHRQUHxwcK9PT9DQ0O/v70w5MLypoG8wKOuwsP/g4P/Q0IcwKEswKMl8aJ9fX2xjdOtGRs/Pz+Dg4GImIP8gIH0sKEAwKKmTiKZ8aB/f39Wsl+LFt8dgUE9PT5x5aHBwcP+AgP+WltdgYMyZfyywz78AAAAAAAD///8AAP9mZv///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAKgALAAAAAA9AEQAAAj/AFEJHEiwoMGDCBMqXMiwocAbBww4nEhxoYkUpzJGrMixogkfGUNqlNixJEIDB0SqHGmyJSojM1bKZOmyop0gM3Oe2liTISKMOoPy7GnwY9CjIYcSRYm0aVKSLmE6nfq05QycVLPuhDrxBlCtYJUqNAq2bNWEBj6ZXRuyxZyDRtqwnXvkhACDV+euTeJm1Ki7A73qNWtFiF+/gA95Gly2CJLDhwEHMOUAAuOpLYDEgBxZ4GRTlC1fDnpkM+fOqD6DDj1aZpITp0dtGCDhr+fVuCu3zlg49ijaokTZTo27uG7Gjn2P+hI8+PDPERoUB318bWbfAJ5sUNFcuGRTYUqV/3ogfXp1rWlMc6awJjiAAd2fm4ogXjz56aypOoIde4OE5u/F9x199dlXnnGiHZWEYbGpsAEA3QXYnHwEFliKAgswgJ8LPeiUXGwedCAKABACCN+EA1pYIIYaFlcDhytd51sGAJbo3onOpajiihlO92KHGaUXGwWjUBChjSPiWJuOO/LYIm4v1tXfE6J4gCSJEZ7YgRYUNrkji9P55sF/ogxw5ZkSqIDaZBV6aSGYq/lGZplndkckZ98xoICbTcIJGQAZcNmdmUc210hs35nCyJ58fgmIKX5RQGOZowxaZwYA+JaoKQwswGijBV4C6SiTUmpphMspJx9unX4KaimjDv9aaXOEBteBqmuuxgEHoLX6Kqx+yXqqBANsgCtit4FWQAEkrNbpq7HSOmtwag5w57GrmlJBASEU18ADjUYb3ADTinIttsgSB1oJFfA63bduimuqKB1keqwUhoCSK374wbujvOSu4QG6UvxBRydcpKsav++Ca6G8A6Pr1x2kVMyHwsVxUALDq/krnrhPSOzXG1lUTIoffqGR7Goi2MAxbv6O2kEG56I7CSlRsEFKFVyovDJoIRTg7sugNRDGqCJzJgcKE0ywc0ELm6KBCCJo8DIPFeCWNGcyqNFE06ToAfV0HBRgxsvLThHn1oddQMrXj5DyAQgjEHSAJMWZwS3HPxT/QMbabI/iBCliMLEJKX2EEkomBAUCxRi42VDADxyTYDVogV+wSChqmKxEKCDAYFDFj4OmwbY7bDGdBhtrnTQYOigeChUmc1K3QTnAUfEgGFgAWt88hKA6aCRIXhxnQ1yg3BCayK44EWdkUQcBByEQChFXfCB776aQsG0BIlQgQgE8qO26X1h8cEUep8ngRBnOy74E9QgRgEAC8SvOfQkh7FDBDmS43PmGoIiKUUEGkMEC/PJHgxw0xH74yx/3XnaYRJgMB8obxQW6kL9QYEJ0FIFgByfIL7/IQAlvQwEpnAC7DtLNJCKUoO/w45c44GwCXiAFB/OXAATQryUxdN4LfFiwgjCNYg+kYMIEFkCKDs6PKAIJouyGWMS1FSKJOMRB/BoIxYJIUXFUxNwoIkEKPAgCBZSQHQ1A2EWDfDEUVLyADj5AChSIQW6gu10bE/JG2VnCZGfo4R4d0sdQoBAHhPjhIB94v/wRoRKQWGRHgrhGSQJxCS+0pCZbEhAAOw==" - - callback(null, { - statusCode: '200', - body: gifImageBase64, - - // API Gateway will automatically convert the base64 encoded response to binary if the Accept header in - // request matches the Content-Type of response. Unfortunately, if you use this in an HTML Image tag - // , then browsers don't send a specific Accept header. Therefore API Gateway - // will return the base64 text response. If serving image in HTML tag is your primary usecase, - // then you can use */* as value for BinaryMediaType which will make API Gateway treat every response - // as binary type, and hence decode base64 always. - isBase64Encoded: true, - headers: { - "Access-Control-Allow-Origin": "https://www.example.com", - "Content-Type": "image/gif" - } - }); - -} diff --git a/examples/2016-10-31/implicit_api_settings/template.yaml b/examples/2016-10-31/implicit_api_settings/template.yaml deleted file mode 100644 index 141b2061e..000000000 --- a/examples/2016-10-31/implicit_api_settings/template.yaml +++ /dev/null @@ -1,87 +0,0 @@ ---- -Transform: AWS::Serverless-2016-10-31 - -Globals: - Api: - # Allows www.example.com to call these APIs - # SAM will automatically add AllowMethods with a list of methods for this API - Cors: "'https://www.www.example.com'" - - # API Gateway regional endpoints - EndpointConfiguration: REGIONAL - - # Send/receive binary data through the APIs - BinaryMediaTypes: - # These are equivalent to image/gif and image/png when deployed - - image~1gif - - image~1png - - # Compression is triggered when response body size is greater than or equal to your configured threshold - MinimumCompressionSize: 1024 - - # Logging, Metrics, Throttling, and all other Stage settings - MethodSettings: [{ - # Turn on Info logging - "LoggingLevel": "INFO", - - # Enable Metrics - "MetricsEnabled": True, - - # Trace-level Logging - "DataTraceEnabled": True, - - # On all Paths & methods - "ResourcePath": "/*", - "HttpMethod": "*", - }] - -Resources: - LambdaFunction: - Type: AWS::Serverless::Function - Properties: - # Replace with your bucket name - CodeUri: src/ - Handler: index.handler - Runtime: nodejs12.x - Events: - ProxyApiRoot: - Type: Api - Properties: - Path: / - Method: ANY - ProxyApiGreedy: - Type: Api - Properties: - Path: /{proxy+} - Method: ANY - - ####### Necessary for API Gateway Logging ######## - # Add the CloudWatchRole and Account resource to your template to give API Gateway permissions write to CloudWatch logs - # https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-apigateway-account.html#aws-resource-apigateway-account-examples - # - # NOTE: This is a one time process. As long as you have this enabled once in a region, you can deploy other stacks - # without the need for each stack to create this role. As a good practice, create a separate stack altogether - # with just the API Gateway logging role so none of your application stacks need them. - ApiGwAccountConfig: - Type: "AWS::ApiGateway::Account" - Properties: - CloudWatchRoleArn: !GetAtt "ApiGatewayLoggingRole.Arn" - ApiGatewayLoggingRole: - Type: "AWS::IAM::Role" - Properties: - AssumeRolePolicyDocument: - Version: "2012-10-17" - Statement: - - Effect: Allow - Principal: - Service: - - "apigateway.amazonaws.com" - Action: "sts:AssumeRole" - Path: "/" - ManagedPolicyArns: - - !Sub "arn:${AWS::Partition}:iam::aws:policy/service-role/AmazonAPIGatewayPushToCloudWatchLogs" - -Outputs: - ApiUrl: - Description: URL of your API endpoint - Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod" diff --git a/examples/2016-10-31/inline_swagger/src/index.js b/examples/2016-10-31/inline_swagger/src/index.js deleted file mode 100644 index 7f7a6018d..000000000 --- a/examples/2016-10-31/inline_swagger/src/index.js +++ /dev/null @@ -1,6 +0,0 @@ -exports.handler = function(event, context, callback) { - callback(null, { - "statusCode": 200, - "body": "hello world" - }); -} diff --git a/examples/2016-10-31/inline_swagger/template.yaml b/examples/2016-10-31/inline_swagger/template.yaml deleted file mode 100644 index 77a03ff65..000000000 --- a/examples/2016-10-31/inline_swagger/template.yaml +++ /dev/null @@ -1,46 +0,0 @@ -AWSTemplateFormatVersion: '2010-09-09' -Transform: AWS::Serverless-2016-10-31 -Description: Simple API Endpoint configured using Swagger specified inline and backed by a Lambda function -Resources: - MyApi: - Type: AWS::Serverless::Api - Properties: - StageName: prod - DefinitionBody: - swagger: 2.0 - info: - title: - Ref: AWS::StackName - paths: - "/get": - get: - x-amazon-apigateway-integration: - httpMethod: POST - type: aws_proxy - uri: - Fn::Sub: arn:${AWS::Partition}:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyLambdaFunction.Arn}/invocations - responses: {} - - - - - MyLambdaFunction: - Type: AWS::Serverless::Function - Properties: - Handler: index.handler - Runtime: nodejs12.x - CodeUri: src/ - Events: - GetApi: - Type: Api - Properties: - Path: /get - Method: GET - RestApiId: - Ref: MyApi - -Outputs: - - ApiURL: - Description: "API endpoint URL for Prod environment" - Value: !Sub "https://${MyApi}.execute-api.${AWS::Region}.amazonaws.com/prod/get" \ No newline at end of file diff --git a/examples/2016-10-31/iot_backend/src/index.js b/examples/2016-10-31/iot_backend/src/index.js deleted file mode 100644 index 9cb0ef786..000000000 --- a/examples/2016-10-31/iot_backend/src/index.js +++ /dev/null @@ -1,30 +0,0 @@ -console.log('Loading function'); - -var AWS = require('aws-sdk'); -var dynamo = new AWS.DynamoDB.DocumentClient(); -var table = process.env.TABLE_NAME; - -exports.handler = function(event, context, callback) { - //console.log('Received event:', JSON.stringify(event, null, 2)); - - var params = { - TableName:table, - Item:{ - "id": event.id, - "thing": event.thing - } - }; - - console.log("Adding a new IoT device..."); - dynamo.put(params, function(err, data) { - if (err) { - console.error("Unable to add device. Error JSON:", JSON.stringify(err, null, 2)); - callback(err); - } else { - console.log("Added device:", JSON.stringify(data, null, 2)); - callback(null,'DynamoDB updated'); - } - }); - - -} diff --git a/examples/2016-10-31/iot_backend/template.yaml b/examples/2016-10-31/iot_backend/template.yaml deleted file mode 100644 index 9aec0be18..000000000 --- a/examples/2016-10-31/iot_backend/template.yaml +++ /dev/null @@ -1,25 +0,0 @@ -AWSTemplateFormatVersion: "2010-09-09" -Transform: AWS::Serverless-2016-10-31 -Description: IoT -> Lambda -> DynamoDB -Resources: - IoTFunction: - Type: AWS::Serverless::Function - Properties: - Handler: index.handler - Runtime: nodejs12.x - CodeUri: src/ - Policies: - - DynamoDBCrudPolicy: - TableName: !Ref Table - Environment: - Variables: - TABLE_NAME: - Ref: Table - Events: - IoT: - Type: IoTRule - Properties: - AwsIotSqlVersion: '2016-03-23' - Sql: "SELECT * FROM 'iot2ddb'" - Table: - Type: AWS::Serverless::SimpleTable diff --git a/examples/2016-10-31/lambda_edge/README.md b/examples/2016-10-31/lambda_edge/README.md deleted file mode 100644 index 4ec0b87f1..000000000 --- a/examples/2016-10-31/lambda_edge/README.md +++ /dev/null @@ -1,106 +0,0 @@ -## Lambda@Edge sample - -This example leverages [SAM Safe Deployments feature](https://github.com/awslabs/serverless-application-model/blob/master/docs/safe_lambda_deployments.rst) in order to ease Lambda@Edge deployments by automatically publishing a new version upon deployments. Since SAM supports standard Cloudformation resources Cloudfront Distribution configuration will be automatically updated as soon as a new Lambda function version is available. - -The following Lambda function snippet uses ``AutoPublishAlias`` property which provides an additional property named `.Version`: - -```yaml -LambdaEdgeFunctionSample: - Type: AWS::Serverless::Function - Properties: - CodeUri: src/ - Runtime: nodejs10.x - Handler: index.handler - Role: !GetAtt LambdaEdgeFunctionRole.Arn - Timeout: 5 - # More info at https://github.com/awslabs/serverless-application-model/blob/master/docs/safe_lambda_deployments.rst - AutoPublishAlias: live -``` - -This also uses `ProvisionedConcurrencyConfig` setting on the lambda function Alias created by AutoPublishAlias: - -```yaml -LambdaEdgeFunctionSample: - Type: AWS::Serverless::Function - Properties: - CodeUri: src/ - Runtime: nodejs10.x - Handler: index.handler - Role: !GetAtt LambdaEdgeFunctionRole.Arn - Timeout: 5 - # More info at https://github.com/awslabs/serverless-application-model/blob/master/docs/safe_lambda_deployments.rst - AutoPublishAlias: live - ProvisionedConcurrencyConfig: !If - - AliasProvisionedConcurrencyEnabled - - ProvisionedConcurrentExecutions: !Ref ProvisionedConcurrency - - !Ref 'AWS::NoValue' -``` - -We must also create a custom IAM Role which allows `lambda.amazonaws.com` and `edgelambda.amazonaws.com` services to assume the role and execute the function. - -```yaml -LambdaEdgeFunctionRole: - Type: "AWS::IAM::Role" - Properties: - Path: "/" - ManagedPolicyArns: - - "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" - AssumeRolePolicyDocument: - Version: "2012-10-17" - Statement: - - Sid: "AllowLambdaServiceToAssumeRole" - Effect: "Allow" - Action: - - "sts:AssumeRole" - Principal: - Service: - - "lambda.amazonaws.com" - - "edgelambda.amazonaws.com" -``` - -We can now configure our [Cloudfront Distribution Lambda Association Property](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloudfront-distribution-lambdafunctionassociation.html) to always reference the latest available Lambda Function Version ARN: - -```yaml -CFDistribution: - Type: AWS::CloudFront::Distribution - Properties: - DistributionConfig: - Enabled: 'true' - .... - DefaultCacheBehavior: - - # Lambda@Edge configuration requires a function version not alias - LambdaFunctionAssociations: - - - EventType: origin-request - # provides {FunctionARN}:{Version} which is exactly what Cloudfront expects - # SAM Benefit here is upon function changes this function version will also be updated in Cloudfront - LambdaFunctionARN: !Ref LambdaEdgeFunctionSample.Version - ... -``` - -In this example, ``LambdaEdgeFunctionSample.Version`` will be evaluated as ``arn:aws:lambda:::function::`` which is expected input for Lambda@Edge. - -### Deploying this sample - -Before you go and deploy this it is important to note that Lambda@Edge expects your Lambda function to be deployed in N.**us-east-1** (N. Virginia) and therefore you must have both a S3 Bucket and this stack to be deployed in **us-east-1**. - -```bash -aws cloudformation package \ - --template-file template.yaml \ - --output-template-file packaged.yaml \ - --s3-bucket S3_BUCKET_IN_US_EAST_1 \ - --region us-east-1 - -aws cloudformation deploy \ - --template-file packaged.yaml \ - --stack-name lambda-edge-sample2 \ - --capabilities CAPABILITY_IAM \ - --region us-east-1 -``` - -If you don't have a S3 bucket in us-east-1 you can quickly create one and replace the placeholder in the command above: - -```bash -aws s3 mb s3://S3-BUCKET-NAME --region us-east-1 -``` diff --git a/examples/2016-10-31/lambda_edge/src/index.js b/examples/2016-10-31/lambda_edge/src/index.js deleted file mode 100644 index 8ccba807d..000000000 --- a/examples/2016-10-31/lambda_edge/src/index.js +++ /dev/null @@ -1,10 +0,0 @@ -'use strict'; - -exports.handler = (evt, ctx, cb) => { - console.log("New request received... adding Hello World HTTP Header"); - var request = evt.Records[0].cf.request; - request.headers['hello'] = [{ key: 'hello', value: 'world'}]; - - cb(null, request); - -} \ No newline at end of file diff --git a/examples/2016-10-31/lambda_edge/template.yaml b/examples/2016-10-31/lambda_edge/template.yaml deleted file mode 100644 index 7cd283233..000000000 --- a/examples/2016-10-31/lambda_edge/template.yaml +++ /dev/null @@ -1,90 +0,0 @@ -AWSTemplateFormatVersion: '2010-09-09' -Transform: AWS::Serverless-2016-10-31 -Description: Sample SAM configuration for Lambda@Edge to ease deployment and further updates -Parameters: - - ProvisionedConcurrency: - Type: String - Default: 10 - EnableAliasProvisionedConcurrency: - Type: String - AllowedValues: - - true - - false - Default: true -Conditions: - AliasProvisionedConcurrencyEnabled: !Equals [!Ref EnableAliasProvisionedConcurrency, true] -Globals: - - Function: - ProvisionedConcurrencyConfig: !If - - AliasProvisionedConcurrencyEnabled - - ProvisionedConcurrentExecutions: !Ref ProvisionedConcurrency - - !Ref 'AWS::NoValue' - -Resources: - - CFDistribution: - Type: AWS::CloudFront::Distribution - Properties: - DistributionConfig: - Enabled: 'true' - Comment: Lambda@Edge SAM Sample - DefaultRootObject: index.html - Origins: - - - Id: MyOrigin - DomainName: aws.amazon.com # https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloudfront-distribution-origin.html - CustomOriginConfig: - HTTPPort: 80 - OriginProtocolPolicy: match-viewer - DefaultCacheBehavior: - TargetOriginId: MyOrigin - # Lambda@Edge configuration requires a function version not alias - LambdaFunctionAssociations: - - - EventType: origin-request - # provides {FunctionARN}:{Version} which is exactly what Cloudfront expects - # SAM Benefit here is upon function changes this function version will also be updated in Cloudfront - LambdaFunctionARN: !Ref LambdaEdgeFunctionSample.Version - ForwardedValues: - QueryString: 'false' - Headers: - - Origin - Cookies: - Forward: none - ViewerProtocolPolicy: allow-all - - LambdaEdgeFunctionSample: - Type: AWS::Serverless::Function - Properties: - CodeUri: src/ - Role: !GetAtt LambdaEdgeFunctionRole.Arn - Runtime: nodejs10.x - Handler: index.handler - Timeout: 5 - # More info at https://github.com/awslabs/serverless-application-model/blob/master/docs/safe_lambda_deployments.rst - AutoPublishAlias: live - AssumeRolePolicyDocument: - Version: "2012-10-17" - Statement: - - Effect: "Allow" - Action: "sts:AssumeRole" - Principal: - Service: - - "lambda.amazonaws.com" - - "edgelambda.amazonaws.com" - -Outputs: - - LambdaEdgeFunctionSample: - Description: Lambda@Edge Sample Function ARN - Value: !GetAtt LambdaEdgeFunctionSample.Arn - - LambdaEdgeFunctionSampleVersion: - Description: Lambda@Edge Sample Function ARN with Version - Value: !Ref LambdaEdgeFunctionSample.Version - - CFDistribution: - Description: Cloudfront Distribution Domain Name - Value: !GetAtt CFDistribution.DomainName diff --git a/examples/2016-10-31/lambda_safe_deployments/src/preTrafficHook.js b/examples/2016-10-31/lambda_safe_deployments/src/preTrafficHook.js deleted file mode 100644 index 8e86b34a8..000000000 --- a/examples/2016-10-31/lambda_safe_deployments/src/preTrafficHook.js +++ /dev/null @@ -1,44 +0,0 @@ -// @ts-check -'use strict'; - -const aws = require('aws-sdk'); -const codedeploy = new aws.CodeDeploy({apiVersion: '2014-10-06'}); - -exports.handler = async (event, context, callback) => { - - console.log("Entering PreTraffic Hook!"); - console.log(JSON.stringify(event)); - - //Read the DeploymentId from the event payload. - let deploymentId = event.DeploymentId; - console.log("deploymentId=" + deploymentId); - - //Read the LifecycleEventHookExecutionId from the event payload - let lifecycleEventHookExecutionId = event.LifecycleEventHookExecutionId; - console.log("lifecycleEventHookExecutionId=" + lifecycleEventHookExecutionId); - - /* - [Perform validation or prewarming steps here] - */ - - // Prepare the validation test results with the deploymentId and - // the lifecycleEventHookExecutionId for AWS CodeDeploy. - let params = { - deploymentId: deploymentId, - lifecycleEventHookExecutionId: lifecycleEventHookExecutionId, - status: 'Succeeded' // status can be 'Succeeded' or 'Failed' - }; - - try { - await codedeploy.putLifecycleEventHookExecutionStatus(params).promise(); - console.log("putLifecycleEventHookExecutionStatus done. executionStatus=[" + params.status + "]"); - return 'Validation test succeeded' - } - catch (err) { - console.log("putLifecycleEventHookExecutionStatus ERROR: " + err); - throw new Error('Validation test failed') - } - -} - -// See more: https://docs.aws.amazon.com/codedeploy/latest/userguide/reference-appspec-file-structure-hooks.html#reference-appspec-file-structure-hooks-section-structure-lambda-sample-function diff --git a/examples/2016-10-31/lambda_safe_deployments/src/safeTest.js b/examples/2016-10-31/lambda_safe_deployments/src/safeTest.js deleted file mode 100644 index ac3eaff1d..000000000 --- a/examples/2016-10-31/lambda_safe_deployments/src/safeTest.js +++ /dev/null @@ -1,9 +0,0 @@ -'use strict'; - - -exports.handler = async (event, context, callback) => { - - console.log("Function loaded! " + context.functionName + ":" + context.functionVersion); - - callback(null, "Success"); -} diff --git a/examples/2016-10-31/lambda_safe_deployments/template.yaml b/examples/2016-10-31/lambda_safe_deployments/template.yaml deleted file mode 100644 index 656ec4784..000000000 --- a/examples/2016-10-31/lambda_safe_deployments/template.yaml +++ /dev/null @@ -1,53 +0,0 @@ -AWSTemplateFormatVersion : '2010-09-09' -Transform: AWS::Serverless-2016-10-31 -Description: A sample Lambda Safe Deployment Application - -Resources: - mySNSTopic: - Type: AWS::SNS::Topic - safeTest: - Type: AWS::Serverless::Function - Properties: - Handler: safeTest.handler - CodeUri: src/ - Runtime: nodejs12.x - AutoPublishAlias: live - DeploymentPreference: - Type: Linear10PercentEvery1Minute - Hooks: - PreTraffic: !Ref preTrafficHook - - TriggerConfigurations: - - TriggerEvents: - - DeploymentSuccess - - DeploymentFailure - TriggerName: TestTrigger - TriggerTargetArn: !Ref mySNSTopic - - - preTrafficHook: - Type: AWS::Serverless::Function - Properties: - Handler: preTrafficHook.handler - CodeUri: src/ - Policies: - - Version: "2012-10-17" - Statement: - - Effect: "Allow" - Action: - - "codedeploy:PutLifecycleEventHookExecutionStatus" - Resource: - !Sub 'arn:${AWS::Partition}:codedeploy:${AWS::Region}:${AWS::AccountId}:deploymentgroup:${ServerlessDeploymentApplication}/*' - - Version: "2012-10-17" - Statement: - - Effect: "Allow" - Action: - - "lambda:InvokeFunction" - Resource: !GetAtt safeTest.Arn - Runtime: nodejs12.x - FunctionName: 'CodeDeployHook_preTrafficHook' - DeploymentPreference: - Enabled: False - Environment: - Variables: - CurrentVersion: !Ref safeTest.Version diff --git a/examples/2016-10-31/lambda_sns_filter_policy/README.md b/examples/2016-10-31/lambda_sns_filter_policy/README.md deleted file mode 100644 index 691a3dc6e..000000000 --- a/examples/2016-10-31/lambda_sns_filter_policy/README.md +++ /dev/null @@ -1,35 +0,0 @@ -# Lambda function + Filtered SNS Subscription Example - -This example shows you how to create a Lambda function with a SNS event source. - -The Lambda function does not receive all messages published to the SNS topic but only a subset. The messages are filtered based on the attributes attached to - the message. - -## Running the example - -Deploy the example into your account: - -```bash -# Replace YOUR_S3_ARTIFACTS_BUCKET with the name of a bucket which already exists in your account -aws cloudformation package --template-file template.yaml --output-template-file template.packaged.yaml --s3-bucket YOUR_S3_ARTIFACTS_BUCKET - -aws cloudformation deploy --template-file ./template.packaged.yaml --stack-name sam-example-lambda-sns-filter-policy --capabilities CAPABILITY_IAM -``` - -The Lambda function will only receive messages with the attribute `sport` set to `football`. - -In the AWS console go to the topic sam-example-lambda-sns-filter-policy and push the Publish to Topic button. -At the bottom of the Publish page you can add message attributes. Add one attribute: -- key: sport -- Attribute type: String -- value: football - -Enter an arbitrary message body and publish the message. -In Cloudwatch the log group /aws/lambda/sam-example-lambda-sns-filter-policy-notification-logger appears and the logging contains the message attributes of -the received message. - -Now publish a couple of other messages with other values for the attribute `sport` or without the attribute `sport`. -The Lambda function will not receive these messages. - -## Additional resources -https://docs.aws.amazon.com/sns/latest/dg/message-filtering.html \ No newline at end of file diff --git a/examples/2016-10-31/lambda_sns_filter_policy/src/index.js b/examples/2016-10-31/lambda_sns_filter_policy/src/index.js deleted file mode 100644 index 07b954e98..000000000 --- a/examples/2016-10-31/lambda_sns_filter_policy/src/index.js +++ /dev/null @@ -1,8 +0,0 @@ -'use strict'; - - -exports.handler = async (event, context, callback) => { - console.log("Message attributes: " + JSON.stringify(event.Records[0].Sns.MessageAttributes)); - - callback(null, "Success"); -}; \ No newline at end of file diff --git a/examples/2016-10-31/lambda_sns_filter_policy/template.yaml b/examples/2016-10-31/lambda_sns_filter_policy/template.yaml deleted file mode 100644 index 6493a2a2e..000000000 --- a/examples/2016-10-31/lambda_sns_filter_policy/template.yaml +++ /dev/null @@ -1,24 +0,0 @@ -AWSTemplateFormatVersion: '2010-09-09' -Transform: AWS::Serverless-2016-10-31 -Description: Lambda function with SNS filter policy -Resources: - NotificationLogger: - Type: AWS::Serverless::Function - Properties: - CodeUri: ./src - Handler: index.handler - Runtime: nodejs12.x - FunctionName: sam-example-lambda-sns-filter-policy-notification-logger - Events: - NotificationTopic: - Type: SNS - Properties: - Topic: !Ref Notifications - FilterPolicy: - sport: - - football - - Notifications: - Type: AWS::SNS::Topic - Properties: - TopicName: sam-example-lambda-sns-filter-policy diff --git a/examples/2016-10-31/nested_app/README.md b/examples/2016-10-31/nested_app/README.md deleted file mode 100644 index dda03072b..000000000 --- a/examples/2016-10-31/nested_app/README.md +++ /dev/null @@ -1,8 +0,0 @@ -## Nested App Example - -This app uses the [twitter event source app](https://serverlessrepo.aws.amazon.com/applications/arn:aws:serverlessrepo:us-east-1:077246666028:applications~aws-serverless-twitter-event-source) as a nested app and logs the tweets received from the nested app. - -All you need to do is supply the desired parameters for this app and deploy. SAM will create a nested stack for any nested app inside of your template with all of the parameters that are passed to it. - -## Installation Instructions - Please refer to the Installation Steps section of the [twitter-event-source application](https://serverlessrepo.aws.amazon.com/applications/arn:aws:serverlessrepo:us-east-1:077246666028:applications~aws-serverless-twitter-event-source) for detailed information regarding how to obtain and use the tokens and secrets for this application. \ No newline at end of file diff --git a/examples/2016-10-31/nested_app/src/app.py b/examples/2016-10-31/nested_app/src/app.py deleted file mode 100644 index 93ef77ec4..000000000 --- a/examples/2016-10-31/nested_app/src/app.py +++ /dev/null @@ -1,7 +0,0 @@ -import logging - -LOGGER = logging.getLogger() -LOGGER.setLevel(logging.INFO) - -def process_tweets(tweets, context): - LOGGER.info("Received tweets: {}".format(tweets)) diff --git a/examples/2016-10-31/nested_app/template.yaml b/examples/2016-10-31/nested_app/template.yaml deleted file mode 100644 index fc4200ff2..000000000 --- a/examples/2016-10-31/nested_app/template.yaml +++ /dev/null @@ -1,56 +0,0 @@ -AWSTemplateFormatVersion: '2010-09-09' -Transform: 'AWS::Serverless-2016-10-31' -Description: This example imports the aws-serverless-twitter-event-source serverless app as a nested app in this serverless application and connects it to a function that will log the tweets sent for the given Twitter search text. -Parameters: - EncryptedAccessToken: - Type: String - Description: Twitter API Access Token encrypted ciphertext blob as a base64-encoded string. - EncryptedAccessTokenSecret: - Type: String - Description: Twitter API Access Token Secret ciphertext blob as a base64-encoded string. - EncryptedConsumerKey: - Type: String - Description: Twitter API Consumer Key encrypted ciphertext blob as a base64-encoded string. - EncryptedConsumerSecret: - Type: String - Description: Twitter API Consumer Secret encrypted ciphertext blob as a base64-encoded string. - DecryptionKeyName: - Type: String - Description: KMS key name of the key used to encrypt the Twitter API parameters. Note, this must be just the key name (UUID), not the full key ARN. It's assumed the key is owned by the same account, in the same region as the app. - SearchText: - Type: String - Description: Non-URL-encoded search text poller should use when querying Twitter Search API. - Default: AWS - -Resources: - TweetLoggerFunction: - Type: 'AWS::Serverless::Function' - Properties: - Handler: app.process_tweets - Runtime: python3.6 - MemorySize: 128 - Timeout: 10 - CodeUri: src/ - TwitterEventSourceApp: - Type: 'AWS::Serverless::Application' - Properties: - Location: - ApplicationId: arn:aws:serverlessrepo:us-east-1:077246666028:applications/aws-serverless-twitter-event-source - SemanticVersion: 1.1.0 - Parameters: # Using default value for PollingFrequencyInMinutes (1) - TweetProcessorFunctionName: !Ref TweetLoggerFunction - BatchSize: 20 - DecryptionKeyName: !Ref DecryptionKeyName - EncryptedAccessToken: !Ref EncryptedAccessToken - EncryptedAccessTokenSecret: !Ref EncryptedAccessTokenSecret - EncryptedConsumerKey: !Ref EncryptedConsumerKey - EncryptedConsumerSecret: !Ref EncryptedConsumerSecret - SearchText: !Sub '${SearchText} -filter:nativeretweets' # filter out retweet records from search results - TimeoutInMinutes: 20 - -Outputs: - TweetLoggerFunctionArn: - Value: !GetAtt TweetLoggerFunction.Arn - TwitterSearchPollerFunctionArn: - # Reference an output from the nested stack: - Value: !GetAtt TwitterEventSourceApp.Outputs.TwitterSearchPollerFunctionArn diff --git a/examples/2016-10-31/policy_templates/all_policy_templates.yaml b/examples/2016-10-31/policy_templates/all_policy_templates.yaml deleted file mode 100644 index 27e05190c..000000000 --- a/examples/2016-10-31/policy_templates/all_policy_templates.yaml +++ /dev/null @@ -1,118 +0,0 @@ -Transform: AWS::Serverless-2016-10-31 -Resources: - MyFunction: - Type: 'AWS::Serverless::Function' - Properties: - CodeUri: src/ - Handler: index.handler - Runtime: nodejs12.x - Policies: - - - SQSPollerPolicy: - QueueName: name - - - LambdaInvokePolicy: - FunctionName: name - - - CloudWatchDescribeAlarmHistoryPolicy: {} - - - CloudWatchPutMetricPolicy: {} - - - EC2DescribePolicy: {} - - - DynamoDBCrudPolicy: - TableName: name - - - DynamoDBReadPolicy: - TableName: name - - - DynamoDBWritePolicy: - TableName: name - - - SESSendBouncePolicy: - IdentityName: name - - - ElasticsearchHttpPostPolicy: - DomainName: name - - - S3ReadPolicy: - BucketName: name - - - S3CrudPolicy: - BucketName: name - - - S3WritePolicy: - BucketName: name - - - AMIDescribePolicy: {} - - - CloudFormationDescribeStacksPolicy: {} - - - RekognitionDetectOnlyPolicy: {} - - - RekognitionNoDataAccessPolicy: - CollectionId: id - - - RekognitionReadPolicy: - CollectionId: id - - - RekognitionWriteOnlyAccessPolicy: - CollectionId: id - - - RekognitionLabelsPolicy: {} - - - SQSSendMessagePolicy: - QueueName: name - - - SNSPublishMessagePolicy: - TopicName: name - - - VPCAccessPolicy: {} - - - DynamoDBStreamReadPolicy: - TableName: name - StreamName: name - - - KinesisStreamReadPolicy: - StreamName: name - - - SESCrudPolicy: - IdentityName: name - - - SNSCrudPolicy: - TopicName: name - - - KinesisCrudPolicy: - StreamName: name - - - KMSDecryptPolicy: - KeyId: keyId - - - KMSEncryptPolicy: - KeyId: keyId - - - SESBulkTemplatedCrudPolicy: - IdentityName: name - - - SESEmailTemplateCrudPolicy: {} - - - FilterLogEventsPolicy: - LogGroupName: name - - - StepFunctionsExecutionPolicy: - StateMachineName: name - - - CodeCommitCrudPolicy: - RepositoryName: name - - - CodeCommitReadPolicy: - RepositoryName: name - - - TextractPolicy: {} - - - TextractDetectAnalyzePolicy: {} - - - TextractGetResultPolicy: {} - - - EventBridgePutEventsPolicy: - EventBusName: name diff --git a/examples/2016-10-31/policy_templates/src/index.js b/examples/2016-10-31/policy_templates/src/index.js deleted file mode 100644 index 300dd0e6d..000000000 --- a/examples/2016-10-31/policy_templates/src/index.js +++ /dev/null @@ -1,6 +0,0 @@ -'use strict'; -console.log('Loading function'); - -exports.handler = (event, context, callback) => { - callback(null, 'Hello World!'); -}; \ No newline at end of file diff --git a/examples/2016-10-31/s3_processor/src/index.js b/examples/2016-10-31/s3_processor/src/index.js deleted file mode 100644 index ad412345b..000000000 --- a/examples/2016-10-31/s3_processor/src/index.js +++ /dev/null @@ -1,26 +0,0 @@ -'use strict'; -console.log('Loading function'); - -let aws = require('aws-sdk'); -let s3 = new aws.S3({ apiVersion: '2006-03-01' }); - -exports.handler = (event, context, callback) => { - // Get the object from the event and show its content type - const bucket = event.Records[0].s3.bucket.name; - const key = decodeURIComponent(event.Records[0].s3.object.key.replace(/\+/g, ' ')); - const params = { - Bucket: bucket, - Key: key - }; - s3.getObject(params, (err, data) => { - if (err) { - console.log(err); - const message = `Error getting object ${key} from bucket ${bucket}. Make sure they exist and your bucket is in the same region as this function.`; - console.log(message); - callback(message); - } else { - console.log('CONTENT TYPE:', data.ContentType); - callback(null, data.ContentType); - } - }); -}; \ No newline at end of file diff --git a/examples/2016-10-31/s3_processor/template.yaml b/examples/2016-10-31/s3_processor/template.yaml deleted file mode 100644 index 6e4852212..000000000 --- a/examples/2016-10-31/s3_processor/template.yaml +++ /dev/null @@ -1,26 +0,0 @@ -AWSTemplateFormatVersion: '2010-09-09' -Transform: AWS::Serverless-2016-10-31 -Description: A function is triggered off an upload to a bucket. It logs the content type of the uploaded object. -Resources: - ProcessorFunction: - Type: AWS::Serverless::Function - Properties: - Handler: index.handler - Runtime: nodejs12.x - CodeUri: src/ - Policies: AmazonS3ReadOnlyAccess - Events: - PhotoUpload: - Type: S3 - Properties: - Bucket: !Ref Bucket - Events: s3:ObjectCreated:* - - Bucket: - Type: AWS::S3::Bucket - -Outputs: - - S3Bucket: - Description: "S3 Bucket name that will trigger a Lambda function upon new objects insertion" - Value: !Ref Bucket diff --git a/examples/2016-10-31/schedule/.gitignore b/examples/2016-10-31/schedule/.gitignore deleted file mode 100644 index d3bbf0f5e..000000000 --- a/examples/2016-10-31/schedule/.gitignore +++ /dev/null @@ -1 +0,0 @@ -packaged.yaml diff --git a/examples/2016-10-31/schedule/README.md b/examples/2016-10-31/schedule/README.md deleted file mode 100644 index 56db94b10..000000000 --- a/examples/2016-10-31/schedule/README.md +++ /dev/null @@ -1,17 +0,0 @@ -## Schedule example - -### Overview - -The `ScheduledFunction` is of type `AWS::Serverless::Function`. This is a Serverless Application Model (SAM) resource that expands into multiple (3) top-level CloudFormation resources: - -- `AWS::Lambda::Function` -- `AWS::Lambda::Permission` -- `AWS::Events::Rule` - -`AWS::Lambda::Function` is the top-level CloudFormation resource to define an Amazon Lambda function. Because we want to schedule the function’s periodic execution, we include an `Events` property on our `AWS::Serverless::Function` resource. This allows us to define the function execution schedule *within* the context of the function’s properties. Behind-the-scenes, the `Events` property expands into a `AWS::Events::Rule` resource with an invocation rate of once every 5 minutes. - -Lastly, in order for the EventBridge API to invoke our function, it needs permissions to do so. `AWS::Lambda::Permission` grants CloudWatch Events the permission to invoke our function. - -### Deployment - -The [AWS SAM CLI](https://github.com/awslabs/aws-sam-cli) lets you locally build, test, and debug serverless applications defined by AWS SAM templates. You can also use SAM CLI to deploy your applications. [Install SAM CLI](https://docs.aws.amazon.com/lambda/latest/dg/sam-cli-requirements.html), and then follow the [Packaging and Deployment instructions](https://docs.aws.amazon.com/lambda/latest/dg/serverless-deploy-wt.html#serverless-deploy) to deploy your application. diff --git a/examples/2016-10-31/schedule/src/index.js b/examples/2016-10-31/schedule/src/index.js deleted file mode 100644 index d77afb94b..000000000 --- a/examples/2016-10-31/schedule/src/index.js +++ /dev/null @@ -1,7 +0,0 @@ -'use strict'; -console.log('Loading function'); - -exports.handler = (event, context, callback) => { - console.log("Invocation with event =", event); - callback(null, 'Everything is ok!'); -}; \ No newline at end of file diff --git a/examples/2016-10-31/schedule/template.yaml b/examples/2016-10-31/schedule/template.yaml deleted file mode 100644 index 8e9667272..000000000 --- a/examples/2016-10-31/schedule/template.yaml +++ /dev/null @@ -1,15 +0,0 @@ -AWSTemplateFormatVersion: '2010-09-09' -Transform: AWS::Serverless-2016-10-31 -Description: A function triggered on a timer. -Resources: - ScheduledFunction: - Type: AWS::Serverless::Function - Properties: - Handler: index.handler - Runtime: nodejs12.x - CodeUri: src/ - Events: - Timer: - Type: Schedule - Properties: - Schedule: rate(5 minutes) \ No newline at end of file diff --git a/examples/2016-10-31/sns_sqs/.gitignore b/examples/2016-10-31/sns_sqs/.gitignore deleted file mode 100644 index 97902fce3..000000000 --- a/examples/2016-10-31/sns_sqs/.gitignore +++ /dev/null @@ -1 +0,0 @@ -transformed-cfn-template.yaml \ No newline at end of file diff --git a/examples/2016-10-31/sns_sqs/README.md b/examples/2016-10-31/sns_sqs/README.md deleted file mode 100644 index 0a31e527c..000000000 --- a/examples/2016-10-31/sns_sqs/README.md +++ /dev/null @@ -1,19 +0,0 @@ -# SNS-SQS Event Source Example - -Example SAM template for processing messages on an SNS-SQS. - -## Running the example - -```bash -# Set YOUR_S3_ARTIFACTS_BUCKET to a bucket you own -YOUR_S3_ARTIFACTS_BUCKET='YOUR_S3_ARTIFACTS_BUCKET'; \ -aws cloudformation package --template-file template.yaml --output-template-file cfn-transformed-template.yaml --s3-bucket $YOUR_S3_ARTIFACTS_BUCKET -aws cloudformation deploy --template-file ./cfn-transformed-template.yaml --stack-name lambda-sns-sqs-processor --capabilities CAPABILITY_IAM -``` - -After your CloudFormation Stack has completed creation, send a message to the SNS topic to see it in action: - -```bash -YOUR_SNS_TOPIC_ARN=arn:aws:sns:us-east-1:[your_account_id]:[your_topic_name]; \ -aws sns publish --target-arn $YOUR_SNS_TOPIC_ARN --message '{ "myMessage": "Hello SAM!" }' -``` diff --git a/examples/2016-10-31/sns_sqs/index.js b/examples/2016-10-31/sns_sqs/index.js deleted file mode 100644 index ee45136b4..000000000 --- a/examples/2016-10-31/sns_sqs/index.js +++ /dev/null @@ -1,7 +0,0 @@ -async function handler (event, context) { - const records = event.Records - console.log(records) - return {} -} - -module.exports.handler = handler \ No newline at end of file diff --git a/examples/2016-10-31/sns_sqs/template.yaml b/examples/2016-10-31/sns_sqs/template.yaml deleted file mode 100644 index 6a53f33f4..000000000 --- a/examples/2016-10-31/sns_sqs/template.yaml +++ /dev/null @@ -1,23 +0,0 @@ -AWSTemplateFormatVersion: '2010-09-09' -Transform: AWS::Serverless-2016-10-31 -Description: Example of processing messages on an SNS-SQS with Lambda -Resources: - MyTopic: - Type: AWS::SNS::Topic - MyFunction: - Type: AWS::Serverless::Function - Properties: - CodeUri: ./index.js - Handler: index.handler - Runtime: nodejs12.x - Events: - MySQSEvent: - Type: SNS - Properties: - Topic: !Ref MyTopic - SqsSubscription: true - -Outputs: - MyTopic: - Description: "MyTopic ARN" - Value: !Ref MyTopic diff --git a/examples/2016-10-31/sqs/.gitignore b/examples/2016-10-31/sqs/.gitignore deleted file mode 100644 index 97902fce3..000000000 --- a/examples/2016-10-31/sqs/.gitignore +++ /dev/null @@ -1 +0,0 @@ -transformed-cfn-template.yaml \ No newline at end of file diff --git a/examples/2016-10-31/sqs/README.md b/examples/2016-10-31/sqs/README.md deleted file mode 100644 index 7c29b96fb..000000000 --- a/examples/2016-10-31/sqs/README.md +++ /dev/null @@ -1,19 +0,0 @@ -# SQS Event Source Example - -Example SAM template for processing messages on an SQS queue. - -## Running the example - -```bash -# Set YOUR_S3_ARTIFACTS_BUCKET to a bucket you own -YOUR_S3_ARTIFACTS_BUCKET='YOUR_S3_ARTIFACTS_BUCKET'; \ -aws cloudformation package --template-file template.yaml --output-template-file cfn-transformed-template.yaml --s3-bucket $YOUR_S3_ARTIFACTS_BUCKET -aws cloudformation deploy --template-file ./cfn-transformed-template.yaml --stack-name lambda-sqs-processor --capabilities CAPABILITY_IAM -``` - -After your CloudFormation Stack has completed creation, send a message to the SQS queue to see it in action: - -```bash -YOUR_SQS_QUEUE_URL=https://sqs.us-east-1.amazonaws.com/123456789012/my-queue; \ -aws sqs send-message --queue-url $YOUR_SQS_QUEUE_URL --message-body '{ "myMessage": "Hello SAM!" }' -``` \ No newline at end of file diff --git a/examples/2016-10-31/sqs/index.js b/examples/2016-10-31/sqs/index.js deleted file mode 100644 index 5859fb25c..000000000 --- a/examples/2016-10-31/sqs/index.js +++ /dev/null @@ -1,10 +0,0 @@ -async function handler (event, context) { - // TODO: Handle message... - const records = event.Records - - console.log(records) - - return {} -} - -module.exports.handler = handler \ No newline at end of file diff --git a/examples/2016-10-31/sqs/template.yaml b/examples/2016-10-31/sqs/template.yaml deleted file mode 100644 index 86749536b..000000000 --- a/examples/2016-10-31/sqs/template.yaml +++ /dev/null @@ -1,19 +0,0 @@ -AWSTemplateFormatVersion: '2010-09-09' -Transform: AWS::Serverless-2016-10-31 -Description: Example of processing messages on an SQS queue with Lambda -Resources: - MySQSQueueFunction: - Type: AWS::Serverless::Function - Properties: - CodeUri: ./index.js - Handler: index.handler - Runtime: nodejs12.x - Events: - MySQSEvent: - Type: SQS - Properties: - Queue: !GetAtt MySqsQueue.Arn - BatchSize: 10 - - MySqsQueue: - Type: AWS::SQS::Queue diff --git a/examples/2016-10-31/stream_processor/src/index.js b/examples/2016-10-31/stream_processor/src/index.js deleted file mode 100644 index ea051babc..000000000 --- a/examples/2016-10-31/stream_processor/src/index.js +++ /dev/null @@ -1,11 +0,0 @@ -'use strict'; -console.log('Loading function'); - -exports.handler = (event, context, callback) => { - event.Records.forEach((record) => { - // Kinesis data is base64 encoded so decode here - const payload = new Buffer(record.kinesis.data, 'base64').toString('ascii'); - console.log('Decoded payload:', payload); - }); - callback(null, `Successfully processed ${event.Records.length} records.`); -}; \ No newline at end of file diff --git a/examples/2016-10-31/stream_processor/template.yaml b/examples/2016-10-31/stream_processor/template.yaml deleted file mode 100644 index 894ea6788..000000000 --- a/examples/2016-10-31/stream_processor/template.yaml +++ /dev/null @@ -1,38 +0,0 @@ -AWSTemplateFormatVersion: '2010-09-09' -Transform: AWS::Serverless-2016-10-31 -Description: A function that processes data from a Kinesis stream. -Resources: - StreamProcessor: - Type: AWS::Serverless::Function - Properties: - Handler: index.handler - Runtime: nodejs12.x - CodeUri: src/ - Events: - Stream: - Type: Kinesis - Properties: - Stream: !GetAtt Stream.Arn - MaximumBatchingWindowInSeconds: 20 - ParallelizationFactor: 8 - MaximumRetryAttempts: 100 - BisectBatchOnFunctionError: true - MaximumRecordAgeInSeconds: 604800 - StartingPosition: TRIM_HORIZON - DestinationConfig: - OnFailure: - Type: SNS - Destination: !Ref MySnsTopic - - Stream: - Type: AWS::Kinesis::Stream - Properties: - ShardCount: 1 - - MySnsTopic: - Type: AWS::SNS::Topic - -Outputs: - KinesisStream: - Description: "Kinesis Stream that will trigger Lambda function upon new records" - Value: !GetAtt Stream.Arn diff --git a/examples/2016-10-31/usage_plan/template.yaml b/examples/2016-10-31/usage_plan/template.yaml deleted file mode 100644 index bd79b6cb7..000000000 --- a/examples/2016-10-31/usage_plan/template.yaml +++ /dev/null @@ -1,80 +0,0 @@ -Parameters: - UsagePlanType: - Type: String - Default: PER_API - -Globals: - Api: - OpenApiVersion: 3.0.0 - Auth: - ApiKeyRequired: true - UsagePlan: - CreateUsagePlan: !Ref UsagePlanType - -Resources: - MyApiOne: - Type: AWS::Serverless::Api - Properties: - StageName: Prod - - MyApiTwo: - Type: AWS::Serverless::Api - Properties: - StageName: Prod - Auth: - UsagePlan: - CreateUsagePlan: SHARED - - MyFunctionOne: - Type: AWS::Serverless::Function - Properties: - Handler: index.handler - Runtime: nodejs12.x - InlineCode: | - exports.handler = async (event) => { - return { - statusCode: 200, - body: JSON.stringify(event), - headers: {} - } - } - Events: - ApiKey: - Type: Api - Properties: - RestApiId: - Ref: MyApiOne - Method: get - Path: /path/one - - MyFunctionTwo: - Type: AWS::Serverless::Function - Properties: - Handler: index.handler - Runtime: nodejs12.x - InlineCode: | - exports.handler = async (event) => { - return { - statusCode: 200, - body: JSON.stringify(event), - headers: {} - } - } - Events: - ApiKey: - Type: Api - Properties: - RestApiId: - Ref: MyApiTwo - Method: get - Path: /path/two -Outputs: - ApiOneUrl: - Description: "API endpoint URL for Prod environment" - Value: - Fn::Sub: 'https://${MyApiOne}.execute-api.${AWS::Region}.amazonaws.com/Prod/' - - ApiTwoUrl: - Description: "API endpoint URL for Prod environment" - Value: - Fn::Sub: 'https://${MyApiTwo}.execute-api.${AWS::Region}.amazonaws.com/Prod/' \ No newline at end of file diff --git a/examples/2016-10-31/workmail-hello-world-python/README.md b/examples/2016-10-31/workmail-hello-world-python/README.md deleted file mode 100644 index 48b2f78a7..000000000 --- a/examples/2016-10-31/workmail-hello-world-python/README.md +++ /dev/null @@ -1,41 +0,0 @@ -# AWS WorkMail hello world - -This is a hello world example of the WorkMail lambda feature. For more information see [AWS WorMail lambda documentation](https://docs.aws.amazon.com/workmail/latest/adminguide/lambda.html) - -To use this application you can deploy it via Lambda console. Visit [AWS Lambda Console](https://console.aws.amazon.com/lambda/home?region=us-east-1#/create?firstrun=true&tab=serverlessApps) - -### Local development - -First, [set up the SAM CLI](https://github.com/awslabs/aws-sam-cli/blob/develop/docs/installation.rst). - -Now, test the application locally using: - -`sam local invoke WorkMailHelloWorldFunction -e event.json` - -### Deploying - -```bash -sam package \ - --template-file template.yaml \ - --output-template-file packaged.yaml \ - --s3-bucket $YOUR_BUCKET_NAME -``` - -```bash -sam deploy \ - --template-file packaged.yaml \ - --stack-name workmail-hello-world \ - --capabilities CAPABILITY_IAM -``` - -### Configure WorkMail -Find the ARN of your new lambda function using: - -```bash -aws cloudformation describe-stacks \ - --stack-name workmail-hello-world \ - --query 'Stacks[].Outputs[0].OutputValue' -``` - -Now you can go to WorkMail console and configure an outbound rule to use your new lambda. - diff --git a/examples/2016-10-31/workmail-hello-world-python/event.json b/examples/2016-10-31/workmail-hello-world-python/event.json deleted file mode 100644 index bdc650882..000000000 --- a/examples/2016-10-31/workmail-hello-world-python/event.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "summaryVersion": "2018-10-10", - "envelope": { - "mailFrom" : { - "address" : "from@domain.test" - }, - "recipients" : [ - { "address" : "recipient1@domain.test" }, - { "address" : "recipient2@domain.test" } - ] - }, - "sender" : { - "address" : "sender@domain.test" - }, - "subject" : "Hello From Amazon WorkMail!", - "truncated": false -} diff --git a/examples/2016-10-31/workmail-hello-world-python/template.yaml b/examples/2016-10-31/workmail-hello-world-python/template.yaml deleted file mode 100644 index 01817cd8c..000000000 --- a/examples/2016-10-31/workmail-hello-world-python/template.yaml +++ /dev/null @@ -1,25 +0,0 @@ -AWSTemplateFormatVersion: '2010-09-09' -Transform: AWS::Serverless-2016-10-31 -Description: > - WorkMail hello world lambda SAM - -Resources: - WorkMailHelloWorldFunction: - Type: AWS::Serverless::Function - Properties: - CodeUri: workmail-hello-world/ - Handler: app.lambda_handler - Runtime: python3.6 - Timeout: 10 - - PermissionToCallLambdaAbove: - Type: AWS::Lambda::Permission - DependsOn: WorkMailHelloWorldFunction - Properties: - Action: lambda:InvokeFunction - FunctionName: !Ref WorkMailHelloWorldFunction - Principal: !Sub 'workmail.${AWS::Region}.amazonaws.com' - -Outputs: - HelloWorldArn: - Value: !GetAtt WorkMailHelloWorldFunction.Arn diff --git a/examples/2016-10-31/workmail-hello-world-python/workmail-hello-world/app.py b/examples/2016-10-31/workmail-hello-world-python/workmail-hello-world/app.py deleted file mode 100644 index e4e434907..000000000 --- a/examples/2016-10-31/workmail-hello-world-python/workmail-hello-world/app.py +++ /dev/null @@ -1,72 +0,0 @@ -""" - -Hello world example for AWS WorkMail - -Parameters ----------- -event: dict, required - AWS WorkMail Message Summary Input Format - For more information, see https://docs.aws.amazon.com/workmail/latest/adminguide/lambda.html - - { - "summaryVersion": "2018-10-10", # AWS WorkMail Message Summary Version - "envelope": { - "mailFrom" : { - "address" : "from@domain.test" # String containing from email address - }, - "recipients" : [ # List of all recipient email addresses - { "address" : "recipient1@domain.test" }, - { "address" : "recipient2@domain.test" } - ] - }, - "sender" : { - "address" : "sender@domain.test" # String containing sender email address - }, - "subject" : "Hello From Amazon WorkMail!", # String containing email subject (Truncated to first 256 chars)" - "truncated": false # boolean indicating if any field in message was truncated due to size limitations - } - -context: object, required - Lambda Context runtime methods and attributes - - Attributes - ---------- - - context.aws_request_id: str - Lambda request ID - context.client_context: object - Additional context when invoked through AWS Mobile SDK - context.function_name: str - Lambda function name - context.function_version: str - Function version identifier - context.get_remaining_time_in_millis: function - Time in milliseconds before function times out - context.identity: - Cognito identity provider context when invoked through AWS Mobile SDK - context.invoked_function_arn: str - Function ARN - context.log_group_name: str - Cloudwatch Log group name - context.log_stream_name: str - Cloudwatch Log stream name - context.memory_limit_in_mb: int - Function memory - - https://docs.aws.amazon.com/lambda/latest/dg/python-context-object.html - -Returns ------- -Nothing -""" -def lambda_handler(event, context): - try: - fromAddress = event['envelope']['mailFrom']['address'] - subject = event['subject'] - print(f"Received Email from {fromAddress} with Subject {subject}") - - except Exception as e: - # Send some context about this error to Lambda Logs - print(e) - raise e - diff --git a/examples/package.json b/examples/package.json deleted file mode 100644 index dc8bd4693..000000000 --- a/examples/package.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "examples", - "version": "1.0.0", - "description": "", - "main": "index.js", - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" - }, - "author": "", - "license": "Apache 2.0", - "devDependencies": { - "eslint": "^5.3.0", - "eslint-config-standard": "^11.0.0", - "eslint-plugin-import": "^2.14.0", - "eslint-plugin-node": "^7.0.1", - "eslint-plugin-promise": "^3.8.0", - "eslint-plugin-standard": "^3.1.0" - } -} diff --git a/samtranslator/__init__.py b/samtranslator/__init__.py index d1067bcb6..d00934fb7 100644 --- a/samtranslator/__init__.py +++ b/samtranslator/__init__.py @@ -1 +1 @@ -__version__ = "1.25.0" +__version__ = "1.26.0" diff --git a/samtranslator/model/api/api_generator.py b/samtranslator/model/api/api_generator.py index 1dc2caa1c..12208209d 100644 --- a/samtranslator/model/api/api_generator.py +++ b/samtranslator/model/api/api_generator.py @@ -290,10 +290,11 @@ def _construct_api_domain(self, rest_api): if endpoint is None: endpoint = "REGIONAL" self.domain["EndpointConfiguration"] = "REGIONAL" - elif endpoint not in ["EDGE", "REGIONAL"]: + elif endpoint not in ["EDGE", "REGIONAL", "PRIVATE"]: raise InvalidResourceException( self.logical_id, - "EndpointConfiguration for Custom Domains must be" " one of {}.".format(["EDGE", "REGIONAL"]), + "EndpointConfiguration for Custom Domains must be" + " one of {}.".format(["EDGE", "REGIONAL", "PRIVATE"]), ) if endpoint == "REGIONAL": @@ -925,6 +926,11 @@ def _set_endpoint_configuration(self, rest_api, value): :param rest_api: RestApi resource :param string/dict value: Value to be set """ - - rest_api.EndpointConfiguration = {"Types": [value]} - rest_api.Parameters = {"endpointConfigurationTypes": value} + if isinstance(value, dict) and value.get("Type"): + rest_api.Parameters = {"endpointConfigurationTypes": value.get("Type")} + rest_api.EndpointConfiguration = {"Types": [value.get("Type")]} + if "VPCEndpointIds" in value.keys(): + rest_api.EndpointConfiguration["VpcEndpointIds"] = value.get("VPCEndpointIds") + else: + rest_api.EndpointConfiguration = {"Types": [value]} + rest_api.Parameters = {"endpointConfigurationTypes": value} diff --git a/samtranslator/model/apigateway.py b/samtranslator/model/apigateway.py index 7494f6729..461d656e0 100644 --- a/samtranslator/model/apigateway.py +++ b/samtranslator/model/apigateway.py @@ -244,6 +244,9 @@ def __init__( "of Headers, QueryStrings, StageVariables, or Context.", ) + if authorization_scopes is not None and not isinstance(authorization_scopes, list): + raise InvalidResourceException(api_logical_id, "AuthorizationScopes must be a list.") + self.api_logical_id = api_logical_id self.name = name self.user_pool_arn = user_pool_arn diff --git a/samtranslator/model/apigatewayv2.py b/samtranslator/model/apigatewayv2.py index 1345cd79a..ecce846df 100644 --- a/samtranslator/model/apigatewayv2.py +++ b/samtranslator/model/apigatewayv2.py @@ -62,6 +62,9 @@ def __init__( """ Creates an authorizer for use in V2 Http Apis """ + if authorization_scopes is not None and not isinstance(authorization_scopes, list): + raise InvalidResourceException(api_logical_id, "AuthorizationScopes must be a list.") + # Currently only one type of auth self.auth_type = "oauth2" diff --git a/samtranslator/model/cognito.py b/samtranslator/model/cognito.py index bd9a83031..d44e1cf3d 100644 --- a/samtranslator/model/cognito.py +++ b/samtranslator/model/cognito.py @@ -6,6 +6,7 @@ class CognitoUserPool(Resource): resource_type = "AWS::Cognito::UserPool" property_types = { + "AccountRecoverySetting": PropertyType(False, is_type(dict)), "AdminCreateUserConfig": PropertyType(False, is_type(dict)), "AliasAttributes": PropertyType(False, list_of(is_str())), "AutoVerifiedAttributes": PropertyType(False, list_of(is_str())), @@ -13,6 +14,7 @@ class CognitoUserPool(Resource): "EmailConfiguration": PropertyType(False, is_type(dict)), "EmailVerificationMessage": PropertyType(False, is_str()), "EmailVerificationSubject": PropertyType(False, is_str()), + "EnabledMfas": PropertyType(False, list_of(is_str())), "LambdaConfig": PropertyType(False, is_type(dict)), "MfaConfiguration": PropertyType(False, is_str()), "Policies": PropertyType(False, is_type(dict)), @@ -21,6 +23,7 @@ class CognitoUserPool(Resource): "SmsConfiguration": PropertyType(False, list_of(dict)), "SmsVerificationMessage": PropertyType(False, is_str()), "UsernameAttributes": PropertyType(False, list_of(is_str())), + "UsernameConfiguration": PropertyType(False, is_type(dict)), "UserPoolAddOns": PropertyType(False, list_of(dict)), "UserPoolName": PropertyType(False, is_str()), "UserPoolTags": PropertyType(False, is_type(dict)), diff --git a/samtranslator/model/eventsources/pull.py b/samtranslator/model/eventsources/pull.py index 66aea268e..f629b56c0 100644 --- a/samtranslator/model/eventsources/pull.py +++ b/samtranslator/model/eventsources/pull.py @@ -10,7 +10,7 @@ class PullEventSource(ResourceMacro): """Base class for pull event sources for SAM Functions. - The pull events are Kinesis Streams, DynamoDB Streams, and SQS Queues. All of these correspond to an + The pull events are Kinesis Streams, DynamoDB Streams, Kafka Streams and SQS Queues. All of these correspond to an EventSourceMapping in Lambda, and require that the execution role be given to Kinesis Streams, DynamoDB Streams, or SQS Queues, respectively. @@ -30,6 +30,7 @@ class PullEventSource(ResourceMacro): "MaximumRecordAgeInSeconds": PropertyType(False, is_type(int)), "DestinationConfig": PropertyType(False, is_type(dict)), "ParallelizationFactor": PropertyType(False, is_type(int)), + "Topics": PropertyType(False, is_type(list)), } def get_policy_arn(self): @@ -61,11 +62,11 @@ def to_cloudformation(self, **kwargs): if not self.Stream and not self.Queue: raise InvalidEventException( - self.relative_id, "No Queue (for SQS) or Stream (for Kinesis or DynamoDB) provided." + self.relative_id, "No Queue (for SQS) or Stream (for Kinesis, DynamoDB or MSK) provided." ) if self.Stream and not self.StartingPosition: - raise InvalidEventException(self.relative_id, "StartingPosition is required for Kinesis and DynamoDB.") + raise InvalidEventException(self.relative_id, "StartingPosition is required for Kinesis, DynamoDB and MSK.") lambda_eventsourcemapping.FunctionName = function_name_or_arn lambda_eventsourcemapping.EventSourceArn = self.Stream or self.Queue @@ -77,6 +78,7 @@ def to_cloudformation(self, **kwargs): lambda_eventsourcemapping.BisectBatchOnFunctionError = self.BisectBatchOnFunctionError lambda_eventsourcemapping.MaximumRecordAgeInSeconds = self.MaximumRecordAgeInSeconds lambda_eventsourcemapping.ParallelizationFactor = self.ParallelizationFactor + lambda_eventsourcemapping.Topics = self.Topics destination_config_policy = None if self.DestinationConfig: @@ -159,3 +161,12 @@ class SQS(PullEventSource): def get_policy_arn(self): return ArnGenerator.generate_aws_managed_policy_arn("service-role/AWSLambdaSQSQueueExecutionRole") + + +class MSK(PullEventSource): + """MSK event source.""" + + resource_type = "MSK" + + def get_policy_arn(self): + return ArnGenerator.generate_aws_managed_policy_arn("service-role/AWSLambdaMSKExecutionRole") diff --git a/samtranslator/model/lambda_.py b/samtranslator/model/lambda_.py index 03c00bc5d..64123ddf4 100644 --- a/samtranslator/model/lambda_.py +++ b/samtranslator/model/lambda_.py @@ -69,6 +69,7 @@ class LambdaEventSourceMapping(Resource): "DestinationConfig": PropertyType(False, is_type(dict)), "ParallelizationFactor": PropertyType(False, is_type(int)), "StartingPosition": PropertyType(False, is_str()), + "Topics": PropertyType(False, is_type(list)), } runtime_attrs = {"name": lambda self: ref(self.logical_id)} diff --git a/samtranslator/model/sam_resources.py b/samtranslator/model/sam_resources.py index 5eef735be..23efe3417 100644 --- a/samtranslator/model/sam_resources.py +++ b/samtranslator/model/sam_resources.py @@ -716,7 +716,7 @@ class SamApi(SamResourceMacro): "CacheClusterEnabled": PropertyType(False, is_type(bool)), "CacheClusterSize": PropertyType(False, is_str()), "Variables": PropertyType(False, is_type(dict)), - "EndpointConfiguration": PropertyType(False, is_str()), + "EndpointConfiguration": PropertyType(False, one_of(is_str(), is_type(dict))), "MethodSettings": PropertyType(False, is_type(list)), "BinaryMediaTypes": PropertyType(False, is_type(list)), "MinimumCompressionSize": PropertyType(False, is_type(int)), diff --git a/samtranslator/swagger/swagger.py b/samtranslator/swagger/swagger.py index 46e01ac4f..418f3e092 100644 --- a/samtranslator/swagger/swagger.py +++ b/samtranslator/swagger/swagger.py @@ -1193,4 +1193,5 @@ def safe_compare_regex_with_string(regex, data): @staticmethod def get_path_without_trailing_slash(path): - return re.sub(r"{([a-zA-Z0-9._-]+|proxy\+)}", "*", path) + # convert greedy paths to such as {greedy+}, {proxy+} to "*" + return re.sub(r"{([a-zA-Z0-9._-]+|[a-zA-Z0-9._-]+\+|proxy\+)}", "*", path) diff --git a/samtranslator/validator/sam_schema/schema.json b/samtranslator/validator/sam_schema/schema.json index c18f10620..19699acae 100644 --- a/samtranslator/validator/sam_schema/schema.json +++ b/samtranslator/validator/sam_schema/schema.json @@ -474,6 +474,9 @@ { "$ref": "#/definitions/AWS::Serverless::Function.KinesisEvent" }, + { + "$ref": "#/definitions/AWS::Serverless::Function.MSKEvent" + }, { "$ref": "#/definitions/AWS::Serverless::Function.SQSEvent" }, @@ -586,6 +589,27 @@ ], "type": "object" }, + "AWS::Serverless::Function.MSKEvent": { + "additionalProperties": false, + "properties": { + "StartingPosition": { + "type": "string" + }, + "Stream": { + "type": "string" + }, + "Topics": { + "type": "array" + } + }, + "required": [ + "StartingPosition", + "Stream", + "Topics" + ], + "type": "object" + }, + "AWS::Serverless::Function.SQSEvent": { "additionalProperties": false, "properties": { diff --git a/tests/model/eventsources/test_api_event_source.py b/tests/model/eventsources/test_api_event_source.py index 040e0aba0..6357e1d69 100644 --- a/tests/model/eventsources/test_api_event_source.py +++ b/tests/model/eventsources/test_api_event_source.py @@ -51,6 +51,23 @@ def test_get_permission_with_trailing_slash(self): self.assertEqual(arn, "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/foo") + @patch("boto3.session.Session.region_name", "eu-west-2") + def test_get_permission_with_path_parameter_to_any_path(self): + self.api_event_source.Path = "/foo/{userId+}" + cfn = self.api_event_source.to_cloudformation(function=self.func, explicit_api={}) + + perm = cfn[0] + self.assertIsInstance(perm, LambdaPermission) + + try: + arn = self._extract_path_from_arn("{}PermissionProd".format(self.logical_id), perm) + except AttributeError: + self.fail("Permission class isn't valid") + + self.assertEqual( + arn, "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/foo/*" + ) + @patch("boto3.session.Session.region_name", "eu-west-2") def test_get_permission_with_path_parameter(self): self.api_event_source.Path = "/foo/{userId}/bar" diff --git a/tests/model/test_api.py b/tests/model/test_api.py new file mode 100644 index 000000000..627bda3d5 --- /dev/null +++ b/tests/model/test_api.py @@ -0,0 +1,19 @@ +from unittest import TestCase +import pytest + +from samtranslator.model import InvalidResourceException +from samtranslator.model.apigateway import ApiGatewayAuthorizer + + +class TestApiGatewayAuthorizer(TestCase): + def test_create_oauth2_auth(self): + auth = ApiGatewayAuthorizer( + api_logical_id="logicalId", name="authName", authorization_scopes=["scope1", "scope2"] + ) + self.assertIsNotNone(auth) + + def test_create_authorizer_fails_with_string_authorization_scopes(self): + with pytest.raises(InvalidResourceException): + auth = ApiGatewayAuthorizer( + api_logical_id="logicalId", name="authName", authorization_scopes="invalid_scope" + ) diff --git a/tests/model/test_api_v2.py b/tests/model/test_api_v2.py index e9fe87d93..d895067c3 100644 --- a/tests/model/test_api_v2.py +++ b/tests/model/test_api_v2.py @@ -12,6 +12,7 @@ def test_create_oauth2_auth(self): name="authName", jwt_configuration={"config": "value"}, id_source="https://example.com", + authorization_scopes=["scope1", "scope2"], ) self.assertEquals(auth.auth_type, "oauth2") @@ -24,3 +25,12 @@ def test_create_authorizer_no_id_source(self): def test_create_authorizer_no_jwt_config(self): with pytest.raises(InvalidResourceException): auth = ApiGatewayV2Authorizer(api_logical_id="logicalId", name="authName", id_source="https://example.com") + + def test_create_authorizer_fails_with_string_authorization_scopes(self): + with pytest.raises(InvalidResourceException): + auth = ApiGatewayV2Authorizer( + api_logical_id="logicalId", + name="authName", + jwt_configuration={"config": "value"}, + authorization_scopes="invalid_scope", + ) diff --git a/tests/translator/input/api_endpoint_configuration_with_vpcendpoint.yaml b/tests/translator/input/api_endpoint_configuration_with_vpcendpoint.yaml new file mode 100644 index 000000000..66b7ba02d --- /dev/null +++ b/tests/translator/input/api_endpoint_configuration_with_vpcendpoint.yaml @@ -0,0 +1,33 @@ +Parameters: + EndpointConfigType: + Type: String + VpcEndpointId: + Type: String + +Globals: + Api: + # Overriding this property for Implicit API + EndpointConfiguration: + Type: { "Ref" : "EndpointConfigType" } + VPCEndpointIds: + - { "Ref": "VpcEndpointId" } +Resources: + ImplicitApiFunction: + Type: AWS::Serverless::Function + Properties: + CodeUri: s3://sam-demo-bucket/member_portal.zip + Handler: index.gethtml + Runtime: nodejs12.x + Events: + GetHtml: + Type: Api + Properties: + Path: / + Method: get + + ExplicitApi: + Type: AWS::Serverless::Api + Properties: + StageName: Prod + DefinitionUri: s3://sam-demo-bucket/webpage_swagger.json + EndpointConfiguration: SomeValue diff --git a/tests/translator/input/error_api_with_invalid_auth_scopes_openapi.yaml b/tests/translator/input/error_api_with_invalid_auth_scopes_openapi.yaml new file mode 100644 index 000000000..b853f9443 --- /dev/null +++ b/tests/translator/input/error_api_with_invalid_auth_scopes_openapi.yaml @@ -0,0 +1,87 @@ +Resources: + MyApiWithCognitoAuth: + Type: "AWS::Serverless::Api" + Properties: + StageName: Prod + OpenApiVersion: '3.0.1' + Auth: + DefaultAuthorizer: MyDefaultCognitoAuth + Authorizers: + MyDefaultCognitoAuth: + UserPoolArn: arn:aws:1 + AuthorizationScopes: + - default.write + - default.read + MyCognitoAuthWithDefaultScopes: + UserPoolArn: arn:aws:2 + AuthorizationScopes: default.delete + + MyFn: + Type: AWS::Serverless::Function + Properties: + CodeUri: s3://bucket/key + Handler: index.handler + Runtime: nodejs12.x + Events: + CognitoAuthorizerWithDefaultScopes: + Type: Api + Properties: + RestApiId: !Ref MyApiWithCognitoAuth + Method: get + Path: /cognitoauthorizerwithdefaultscopes + Auth: + Authorizer: MyCognitoAuthWithDefaultScopes + CognitoDefaultScopesDefaultAuthorizer: + Type: Api + Properties: + RestApiId: !Ref MyApiWithCognitoAuth + Method: get + Path: /cognitodefaultscopesdefaultauthorizer + CognitoWithAuthNone: + Type: Api + Properties: + RestApiId: !Ref MyApiWithCognitoAuth + Method: get + Path: /cognitowithauthnone + Auth: + Authorizer: NONE + CognitoDefaultScopesWithOverwritten: + Type: Api + Properties: + RestApiId: !Ref MyApiWithCognitoAuth + Method: get + Path: /cognitodefaultscopesoverwritten + Auth: + Authorizer: MyDefaultCognitoAuth + AuthorizationScopes: + - overwritten.read + - overwritten.write + CognitoAuthorizerScopesOverwritten: + Type: Api + Properties: + RestApiId: !Ref MyApiWithCognitoAuth + Method: get + Path: /cognitoauthorizercopesoverwritten + Auth: + Authorizer: MyCognitoAuthWithDefaultScopes + AuthorizationScopes: + - overwritten.read + - overwritten.write + CognitoDefaultScopesNone: + Type: Api + Properties: + RestApiId: !Ref MyApiWithCognitoAuth + Method: get + Path: /cognitodefaultscopesnone + Auth: + Authorizer: MyDefaultCognitoAuth + AuthorizationScopes: [] + CognitoDefaultAuthDefaultScopesNone: + Type: Api + Properties: + RestApiId: !Ref MyApiWithCognitoAuth + Method: get + Path: /cognitodefaultauthdefaultscopesnone + Auth: + Authorizer: MyCognitoAuthWithDefaultScopes + AuthorizationScopes: [] diff --git a/tests/translator/input/streams.yaml b/tests/translator/input/streams.yaml index ff2b54da2..3ae736b79 100644 --- a/tests/translator/input/streams.yaml +++ b/tests/translator/input/streams.yaml @@ -26,3 +26,17 @@ Resources: Stream: arn:aws:dynamodb:us-west-2:012345678901:table/TestTable/stream/2015-05-11T21:21:33.291 BatchSize: 200 StartingPosition: LATEST + MSKFunction: + Type: 'AWS::Serverless::Function' + Properties: + CodeUri: s3://sam-demo-bucket/streams.zip + Handler: stream.msk_handler + Runtime: python2.7 + Events: + MyMSKStream: + Type: MSK + Properties: + Stream: arn:aws:kafka:us-west-2:012345678901:cluster/mycluster/6cc0432b-8618-4f44-bccc-e1fbd8fb7c4d-2 + StartingPosition: LATEST + Topics: + - "Topic1" diff --git a/tests/translator/output/actual b/tests/translator/output/actual new file mode 100644 index 000000000..06bfdfc16 --- /dev/null +++ b/tests/translator/output/actual @@ -0,0 +1,194 @@ +{ + "Parameters": { + "VpcEndpointId": { + "Type": "String" + }, + "EndpointConfigType": { + "Type": "String" + } + }, + "Resources": { + "ImplicitApiFunction": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Handler": "index.gethtml", + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "member_portal.zip" + }, + "Role": { + "Fn::GetAtt": [ + "ImplicitApiFunctionRole", + "Arn" + ] + }, + "Runtime": "nodejs12.x", + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + } + }, + "ImplicitApiFunctionRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ] + }, + "ManagedPolicyArns": [ + "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + } + }, + "ExplicitApiDeploymentf117c932f7": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "RestApiId": { + "Ref": "ExplicitApi" + }, + "Description": "RestApi deployment id: f117c932f75cfa87d23dfed64e9430d0081ef289", + "StageName": "Stage" + } + }, + "ImplicitApiFunctionGetHtmlPermissionProd": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "ImplicitApiFunction" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "ServerlessRestApi" + } + } + ] + } + } + }, + "ExplicitApi": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "EndpointConfiguration": { + "Types": [ + "SomeValue" + ] + }, + "BodyS3Location": { + "Bucket": "sam-demo-bucket", + "Key": "webpage_swagger.json" + }, + "Parameters": { + "endpointConfigurationTypes": "SomeValue" + } + } + }, + "ExplicitApiProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "ExplicitApiDeploymentf117c932f7" + }, + "RestApiId": { + "Ref": "ExplicitApi" + }, + "StageName": "Prod" + } + }, + "ServerlessRestApiProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "ServerlessRestApiDeployment62b96c1a61" + }, + "RestApiId": { + "Ref": "ServerlessRestApi" + }, + "StageName": "Prod" + } + }, + "ServerlessRestApiDeployment62b96c1a61": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "RestApiId": { + "Ref": "ServerlessRestApi" + }, + "Description": "RestApi deployment id: 62b96c1a611878eefb13e8ef66dbc71b9ba3dd19", + "StageName": "Stage" + } + }, + "ServerlessRestApi": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/": { + "get": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ImplicitApiFunction.Arn}/invocations" + } + }, + "responses": {} + } + } + }, + "swagger": "2.0" + }, + "EndpointConfiguration": { + "VpcEndpointIds": [ + { + "Ref": "VpcEndpointId" + } + ], + "Types": [ + { + "Ref": "EndpointConfigType" + } + ] + }, + "Parameters": { + "endpointConfigurationTypes": [ + { + "Ref": "EndpointConfigType" + } + ] + } + } + } + } +} diff --git a/tests/translator/output/api_endpoint_configuration_with_vpcendpoint.json b/tests/translator/output/api_endpoint_configuration_with_vpcendpoint.json new file mode 100644 index 000000000..3fd972822 --- /dev/null +++ b/tests/translator/output/api_endpoint_configuration_with_vpcendpoint.json @@ -0,0 +1,192 @@ +{ + "Parameters": { + "VpcEndpointId": { + "Type": "String" + }, + "EndpointConfigType": { + "Type": "String" + } + }, + "Resources": { + "ImplicitApiFunction": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Handler": "index.gethtml", + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "member_portal.zip" + }, + "Role": { + "Fn::GetAtt": [ + "ImplicitApiFunctionRole", + "Arn" + ] + }, + "Runtime": "nodejs12.x", + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + } + }, + "ImplicitApiFunctionRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "ManagedPolicyArns": [ + "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ], + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ] + } + } + }, + "ExplicitApiDeploymentf117c932f7": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "RestApiId": { + "Ref": "ExplicitApi" + }, + "Description": "RestApi deployment id: f117c932f75cfa87d23dfed64e9430d0081ef289", + "StageName": "Stage" + } + }, + "ImplicitApiFunctionGetHtmlPermissionProd": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "ImplicitApiFunction" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "ServerlessRestApi" + } + } + ] + } + } + }, + "ExplicitApi": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "EndpointConfiguration": { + "Types": [ + "SomeValue" + ] + }, + "BodyS3Location": { + "Bucket": "sam-demo-bucket", + "Key": "webpage_swagger.json" + }, + "Parameters": { + "endpointConfigurationTypes": "SomeValue" + } + } + }, + "ExplicitApiProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "ExplicitApiDeploymentf117c932f7" + }, + "RestApiId": { + "Ref": "ExplicitApi" + }, + "StageName": "Prod" + } + }, + "ServerlessRestApiProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "ServerlessRestApiDeployment62b96c1a61" + }, + "RestApiId": { + "Ref": "ServerlessRestApi" + }, + "StageName": "Prod" + } + }, + "ServerlessRestApiDeployment62b96c1a61": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "RestApiId": { + "Ref": "ServerlessRestApi" + }, + "Description": "RestApi deployment id: 62b96c1a611878eefb13e8ef66dbc71b9ba3dd19", + "StageName": "Stage" + } + }, + "ServerlessRestApi": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/": { + "get": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ImplicitApiFunction.Arn}/invocations" + } + }, + "responses": {} + } + } + }, + "swagger": "2.0" + }, + "EndpointConfiguration": { + "VpcEndpointIds": [ + { + "Ref": "VpcEndpointId" + } + ], + "Types": [ + { + "Ref": "EndpointConfigType" + } + ] + }, + "Parameters": { + "endpointConfigurationTypes": { + "Ref": "EndpointConfigType" + } + } + } + } + } +} diff --git a/tests/translator/output/aws-cn/api_endpoint_configuration_with_vpcendpoint.json b/tests/translator/output/aws-cn/api_endpoint_configuration_with_vpcendpoint.json new file mode 100644 index 000000000..865c90473 --- /dev/null +++ b/tests/translator/output/aws-cn/api_endpoint_configuration_with_vpcendpoint.json @@ -0,0 +1,192 @@ +{ + "Parameters": { + "VpcEndpointId": { + "Type": "String" + }, + "EndpointConfigType": { + "Type": "String" + } + }, + "Resources": { + "ImplicitApiFunction": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Handler": "index.gethtml", + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "member_portal.zip" + }, + "Role": { + "Fn::GetAtt": [ + "ImplicitApiFunctionRole", + "Arn" + ] + }, + "Runtime": "nodejs12.x", + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + } + }, + "ImplicitApiFunctionRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "ManagedPolicyArns": [ + "arn:aws-cn:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ], + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ] + } + } + }, + "ImplicitApiFunctionGetHtmlPermissionProd": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "ImplicitApiFunction" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws-cn:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "ServerlessRestApi" + } + } + ] + } + } + }, + "ExplicitApi": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "EndpointConfiguration": { + "Types": [ + "SomeValue" + ] + }, + "BodyS3Location": { + "Bucket": "sam-demo-bucket", + "Key": "webpage_swagger.json" + }, + "Parameters": { + "endpointConfigurationTypes": "SomeValue" + } + } + }, + "ExplicitApiProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "ExplicitApiDeploymentf117c932f7" + }, + "RestApiId": { + "Ref": "ExplicitApi" + }, + "StageName": "Prod" + } + }, + "ServerlessRestApiProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "ServerlessRestApiDeploymentcb4fb12558" + }, + "RestApiId": { + "Ref": "ServerlessRestApi" + }, + "StageName": "Prod" + } + }, + "ServerlessRestApiDeploymentcb4fb12558": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "RestApiId": { + "Ref": "ServerlessRestApi" + }, + "Description": "RestApi deployment id: cb4fb1255811b7b6a25dd35f23ee7ad133003b89", + "StageName": "Stage" + } + }, + "ExplicitApiDeploymentf117c932f7": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "RestApiId": { + "Ref": "ExplicitApi" + }, + "Description": "RestApi deployment id: f117c932f75cfa87d23dfed64e9430d0081ef289", + "StageName": "Stage" + } + }, + "ServerlessRestApi": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/": { + "get": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws-cn:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ImplicitApiFunction.Arn}/invocations" + } + }, + "responses": {} + } + } + }, + "swagger": "2.0" + }, + "EndpointConfiguration": { + "VpcEndpointIds": [ + { + "Ref": "VpcEndpointId" + } + ], + "Types": [ + { + "Ref": "EndpointConfigType" + } + ] + }, + "Parameters": { + "endpointConfigurationTypes": { + "Ref": "EndpointConfigType" + } + } + } + } + } +} diff --git a/tests/translator/output/aws-cn/streams.json b/tests/translator/output/aws-cn/streams.json index fd19ed8f7..ce73f1a36 100644 --- a/tests/translator/output/aws-cn/streams.json +++ b/tests/translator/output/aws-cn/streams.json @@ -42,6 +42,27 @@ }] } }, + "MSKFunction": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "streams.zip" + }, + "Handler": "stream.msk_handler", + "Role": { + "Fn::GetAtt": [ + "MSKFunctionRole", + "Arn" + ] + }, + "Runtime": "python2.7", + "Tags": [{ + "Value": "SAM", + "Key": "lambda:createdBy" + }] + } + }, "DynamoDBFunctionMyDDBStream": { "Type": "AWS::Lambda::EventSourceMapping", "Properties": { @@ -122,6 +143,46 @@ }] } } + }, + "MSKFunctionMyMSKStream": { + "Type": "AWS::Lambda::EventSourceMapping", + "Properties": { + "EventSourceArn": "arn:aws:kafka:us-west-2:012345678901:cluster/mycluster/6cc0432b-8618-4f44-bccc-e1fbd8fb7c4d-2", + "FunctionName": { + "Ref": "MSKFunction" + }, + "StartingPosition": "LATEST", + "Topics": ["Topic1"] + } + }, + "MSKFunctionRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "ManagedPolicyArns": [ + "arn:aws-cn:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", + "arn:aws-cn:iam::aws:policy/service-role/AWSLambdaMSKExecutionRole" + ], + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ], + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [{ + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + }] + } + } } } } \ No newline at end of file diff --git a/tests/translator/output/aws-us-gov/api_endpoint_configuration_with_vpcendpoint.json b/tests/translator/output/aws-us-gov/api_endpoint_configuration_with_vpcendpoint.json new file mode 100644 index 000000000..45af2dde5 --- /dev/null +++ b/tests/translator/output/aws-us-gov/api_endpoint_configuration_with_vpcendpoint.json @@ -0,0 +1,192 @@ +{ + "Parameters": { + "VpcEndpointId": { + "Type": "String" + }, + "EndpointConfigType": { + "Type": "String" + } + }, + "Resources": { + "ImplicitApiFunction": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Handler": "index.gethtml", + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "member_portal.zip" + }, + "Role": { + "Fn::GetAtt": [ + "ImplicitApiFunctionRole", + "Arn" + ] + }, + "Runtime": "nodejs12.x", + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + } + }, + "ImplicitApiFunctionRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "ManagedPolicyArns": [ + "arn:aws-us-gov:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ], + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ] + } + } + }, + "ImplicitApiFunctionGetHtmlPermissionProd": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "ImplicitApiFunction" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws-us-gov:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "ServerlessRestApi" + } + } + ] + } + } + }, + "ExplicitApi": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "EndpointConfiguration": { + "Types": [ + "SomeValue" + ] + }, + "BodyS3Location": { + "Bucket": "sam-demo-bucket", + "Key": "webpage_swagger.json" + }, + "Parameters": { + "endpointConfigurationTypes": "SomeValue" + } + } + }, + "ExplicitApiProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "ExplicitApiDeploymentf117c932f7" + }, + "RestApiId": { + "Ref": "ExplicitApi" + }, + "StageName": "Prod" + } + }, + "ServerlessRestApiDeployment5b2cb4ba8f": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "RestApiId": { + "Ref": "ServerlessRestApi" + }, + "Description": "RestApi deployment id: 5b2cb4ba8fce8a9445b1914c6c6fbeef81a9075a", + "StageName": "Stage" + } + }, + "ExplicitApiDeploymentf117c932f7": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "RestApiId": { + "Ref": "ExplicitApi" + }, + "Description": "RestApi deployment id: f117c932f75cfa87d23dfed64e9430d0081ef289", + "StageName": "Stage" + } + }, + "ServerlessRestApiProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "ServerlessRestApiDeployment5b2cb4ba8f" + }, + "RestApiId": { + "Ref": "ServerlessRestApi" + }, + "StageName": "Prod" + } + }, + "ServerlessRestApi": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/": { + "get": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws-us-gov:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ImplicitApiFunction.Arn}/invocations" + } + }, + "responses": {} + } + } + }, + "swagger": "2.0" + }, + "EndpointConfiguration": { + "VpcEndpointIds": [ + { + "Ref": "VpcEndpointId" + } + ], + "Types": [ + { + "Ref": "EndpointConfigType" + } + ] + }, + "Parameters": { + "endpointConfigurationTypes": { + "Ref": "EndpointConfigType" + } + } + } + } + } +} diff --git a/tests/translator/output/aws-us-gov/streams.json b/tests/translator/output/aws-us-gov/streams.json index c239a1f0e..b87115674 100644 --- a/tests/translator/output/aws-us-gov/streams.json +++ b/tests/translator/output/aws-us-gov/streams.json @@ -42,6 +42,27 @@ }] } }, + "MSKFunction": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "streams.zip" + }, + "Handler": "stream.msk_handler", + "Role": { + "Fn::GetAtt": [ + "MSKFunctionRole", + "Arn" + ] + }, + "Runtime": "python2.7", + "Tags": [{ + "Value": "SAM", + "Key": "lambda:createdBy" + }] + } + }, "DynamoDBFunctionMyDDBStream": { "Type": "AWS::Lambda::EventSourceMapping", "Properties": { @@ -122,6 +143,46 @@ }] } } + }, + "MSKFunctionMyMSKStream": { + "Type": "AWS::Lambda::EventSourceMapping", + "Properties": { + "EventSourceArn": "arn:aws:kafka:us-west-2:012345678901:cluster/mycluster/6cc0432b-8618-4f44-bccc-e1fbd8fb7c4d-2", + "FunctionName": { + "Ref": "MSKFunction" + }, + "StartingPosition": "LATEST", + "Topics": ["Topic1"] + } + }, + "MSKFunctionRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "ManagedPolicyArns": [ + "arn:aws-us-gov:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", + "arn:aws-us-gov:iam::aws:policy/service-role/AWSLambdaMSKExecutionRole" + ], + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ], + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [{ + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + }] + } + } } } } \ No newline at end of file diff --git a/tests/translator/output/error_api_with_custom_domains_invalid.json b/tests/translator/output/error_api_with_custom_domains_invalid.json index d3b1fccfa..111c9b36e 100644 --- a/tests/translator/output/error_api_with_custom_domains_invalid.json +++ b/tests/translator/output/error_api_with_custom_domains_invalid.json @@ -1,8 +1,8 @@ { "errors": [ { - "errorMessage": "Resource with id [MyApi] is invalid. EndpointConfiguration for Custom Domains must be one of ['EDGE', 'REGIONAL']. Resource with id [ServerlessRestApi] is invalid. Custom Domains only works if both DomainName and CertificateArn are provided." + "errorMessage": "Resource with id [MyApi] is invalid. EndpointConfiguration for Custom Domains must be one of ['EDGE', 'REGIONAL', 'PRIVATE']. Resource with id [ServerlessRestApi] is invalid. Custom Domains only works if both DomainName and CertificateArn are provided." } ], - "errorMessage": "Invalid Serverless Application Specification document. Number of errors found: 2. Resource with id [MyApi] is invalid. EndpointConfiguration for Custom Domains must be one of ['EDGE', 'REGIONAL']. Resource with id [ServerlessRestApi] is invalid. Custom Domains only works if both DomainName and CertificateArn are provided." + "errorMessage": "Invalid Serverless Application Specification document. Number of errors found: 2. Resource with id [MyApi] is invalid. EndpointConfiguration for Custom Domains must be one of ['EDGE', 'REGIONAL', 'PRIVATE']. Resource with id [ServerlessRestApi] is invalid. Custom Domains only works if both DomainName and CertificateArn are provided." } diff --git a/tests/translator/output/error_api_with_invalid_auth_scopes_openapi.json b/tests/translator/output/error_api_with_invalid_auth_scopes_openapi.json new file mode 100644 index 000000000..381daa6e1 --- /dev/null +++ b/tests/translator/output/error_api_with_invalid_auth_scopes_openapi.json @@ -0,0 +1,9 @@ +{ + "errors": [ + { + "errorMessage": "Resource with id [MyApiWithCognitoAuth] is invalid. AuthorizationScopes must be a list." + } + ], + "errorMessage": "Invalid Serverless Application Specification document. Number of errors found: 1. Resource with id [MyApiWithCognitoAuth] is invalid. AuthorizationScopes must be a list." + } + diff --git a/tests/translator/output/error_missing_queue.json b/tests/translator/output/error_missing_queue.json index f92bbf80e..561dfefcf 100644 --- a/tests/translator/output/error_missing_queue.json +++ b/tests/translator/output/error_missing_queue.json @@ -1,6 +1,6 @@ { "errors": [{ - "errorMessage": "Resource with id [SQSFunction] is invalid. Event with id [MySqsQueue] is invalid. No Queue (for SQS) or Stream (for Kinesis or DynamoDB) provided." + "errorMessage": "Resource with id [SQSFunction] is invalid. Event with id [MySqsQueue] is invalid. No Queue (for SQS) or Stream (for Kinesis, DynamoDB or MSK) provided." }], - "errorMessage": "Invalid Serverless Application Specification document. Number of errors found: 1. Resource with id [SQSFunction] is invalid. Event with id [MySqsQueue] is invalid. No Queue (for SQS) or Stream (for Kinesis or DynamoDB) provided." + "errorMessage": "Invalid Serverless Application Specification document. Number of errors found: 1. Resource with id [SQSFunction] is invalid. Event with id [MySqsQueue] is invalid. No Queue (for SQS) or Stream (for Kinesis, DynamoDB or MSK) provided." } \ No newline at end of file diff --git a/tests/translator/output/error_missing_startingposition.json b/tests/translator/output/error_missing_startingposition.json index 4e3da968c..986f8b072 100644 --- a/tests/translator/output/error_missing_startingposition.json +++ b/tests/translator/output/error_missing_startingposition.json @@ -1,6 +1,6 @@ { "errors": [{ - "errorMessage": "Resource with id [KinesisFunction] is invalid. Event with id [MyKinesisStream] is invalid. StartingPosition is required for Kinesis and DynamoDB." + "errorMessage": "Resource with id [KinesisFunction] is invalid. Event with id [MyKinesisStream] is invalid. StartingPosition is required for Kinesis, DynamoDB and MSK." }], - "errorMessage": "Invalid Serverless Application Specification document. Number of errors found: 1. Resource with id [KinesisFunction] is invalid. Event with id [MyKinesisStream] is invalid. StartingPosition is required for Kinesis and DynamoDB." + "errorMessage": "Invalid Serverless Application Specification document. Number of errors found: 1. Resource with id [KinesisFunction] is invalid. Event with id [MyKinesisStream] is invalid. StartingPosition is required for Kinesis, DynamoDB and MSK." } \ No newline at end of file diff --git a/tests/translator/output/error_missing_stream.json b/tests/translator/output/error_missing_stream.json index d8925a773..3dd94a47e 100644 --- a/tests/translator/output/error_missing_stream.json +++ b/tests/translator/output/error_missing_stream.json @@ -1,6 +1,6 @@ { "errors": [{ - "errorMessage": "Resource with id [DynamoDBFunction] is invalid. Event with id [MyDDBStream] is invalid. No Queue (for SQS) or Stream (for Kinesis or DynamoDB) provided." + "errorMessage": "Resource with id [DynamoDBFunction] is invalid. Event with id [MyDDBStream] is invalid. No Queue (for SQS) or Stream (for Kinesis, DynamoDB or MSK) provided." }], - "errorMessage": "Invalid Serverless Application Specification document. Number of errors found: 1. Resource with id [DynamoDBFunction] is invalid. Event with id [MyDDBStream] is invalid. No Queue (for SQS) or Stream (for Kinesis or DynamoDB) provided." + "errorMessage": "Invalid Serverless Application Specification document. Number of errors found: 1. Resource with id [DynamoDBFunction] is invalid. Event with id [MyDDBStream] is invalid. No Queue (for SQS) or Stream (for Kinesis, DynamoDB or MSK) provided." } \ No newline at end of file diff --git a/tests/translator/output/streams.json b/tests/translator/output/streams.json index 3b54cd853..b894b4206 100644 --- a/tests/translator/output/streams.json +++ b/tests/translator/output/streams.json @@ -42,6 +42,27 @@ }] } }, + "MSKFunction": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "streams.zip" + }, + "Handler": "stream.msk_handler", + "Role": { + "Fn::GetAtt": [ + "MSKFunctionRole", + "Arn" + ] + }, + "Runtime": "python2.7", + "Tags": [{ + "Value": "SAM", + "Key": "lambda:createdBy" + }] + } + }, "DynamoDBFunctionMyDDBStream": { "Type": "AWS::Lambda::EventSourceMapping", "Properties": { @@ -122,6 +143,46 @@ }] } } + }, + "MSKFunctionMyMSKStream": { + "Type": "AWS::Lambda::EventSourceMapping", + "Properties": { + "EventSourceArn": "arn:aws:kafka:us-west-2:012345678901:cluster/mycluster/6cc0432b-8618-4f44-bccc-e1fbd8fb7c4d-2", + "FunctionName": { + "Ref": "MSKFunction" + }, + "StartingPosition": "LATEST", + "Topics": ["Topic1"] + } + }, + "MSKFunctionRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "ManagedPolicyArns": [ + "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", + "arn:aws:iam::aws:policy/service-role/AWSLambdaMSKExecutionRole" + ], + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ], + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [{ + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + }] + } + } } } } \ No newline at end of file diff --git a/tests/translator/test_translator.py b/tests/translator/test_translator.py index d703489ef..ce8400b6e 100644 --- a/tests/translator/test_translator.py +++ b/tests/translator/test_translator.py @@ -162,6 +162,7 @@ class TestTranslatorEndToEnd(TestCase): "implicit_api", "explicit_api", "api_endpoint_configuration", + "api_endpoint_configuration_with_vpcendpoint", "api_with_auth_all_maximum", "api_with_auth_all_minimum", "api_with_auth_no_default", @@ -628,6 +629,7 @@ def _generate_new_deployment_hash(self, logical_id, dict_to_hash, rest_api_to_sw "error_function_with_invalid_condition_name", "error_invalid_document_empty_semantic_version", "error_api_with_invalid_open_api_version_type", + "error_api_with_invalid_auth_scopes_openapi", "error_api_with_custom_domains_invalid", "error_api_with_custom_domains_route53_invalid", "error_api_event_import_vaule_reference", diff --git a/tests/translator/validator/test_validator.py b/tests/translator/validator/test_validator.py index ad6d14266..d941d06ee 100644 --- a/tests/translator/validator/test_validator.py +++ b/tests/translator/validator/test_validator.py @@ -22,6 +22,7 @@ "implicit_api", "explicit_api", "api_endpoint_configuration", + "api_endpoint_configuration_with_vpcendpoint", "api_with_method_settings", "api_with_binary_media_types", "api_with_minimum_compression_size", @@ -80,6 +81,7 @@ def test_validate_template_success(testcase): # These templates are failing validation, will fix schema one at a time excluded = [ "api_endpoint_configuration", + "api_endpoint_configuration_with_vpcendpoint", "api_with_binary_media_types", "api_with_minimum_compression_size", "api_with_cors", diff --git a/versions/2016-10-31.md b/versions/2016-10-31.md index 6b02f7fc5..0ccaefa76 100644 --- a/versions/2016-10-31.md +++ b/versions/2016-10-31.md @@ -233,7 +233,7 @@ CacheClusterSize | `string` | The stage's cache cluster size. Variables | Map of `string` to `string` | A map (string to string map) that defines the stage variables, where the variable name is the key and the variable value is the value. Variable names are limited to alphanumeric characters. Values must match the following regular expression: `[A-Za-z0-9._~:/?#&=,-]+`. MethodSettings | List of [CloudFormation MethodSettings property](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apitgateway-stage-methodsetting.html) | Configures all settings for API stage including Logging, Metrics, CacheTTL, Throttling. This value is passed through to CloudFormation. So any values supported by CloudFormation ``MethodSettings`` property can be used here. Tags | Map of `string` to `string` | A map (string to string) that specifies the tags to be added to this API Stage. Keys and values are limited to alphanumeric characters. -EndpointConfiguration | `string` | Specify the type of endpoint for API endpoint. Value is either `REGIONAL`, `EDGE`, or `PRIVATE`. +EndpointConfiguration | `string` or [API EndpointConfiguration Object](#api-endpointconfiguration-object) | Specify the type of endpoint for API endpoint. Specify the type as `REGIONAL` or `EDGE`. To use a `PRIVATE` endpoint, specify a dictionary with additional [API EndpointConfiguration Object](#api-endpointconfiguration-object). (See examples in [template.yaml](../examples/2016-10-31/api_endpointconfiguration/template.yaml)) BinaryMediaTypes | List of `string` | List of MIME types that your API could return. Use this to enable binary support for APIs. Use `~1` instead of `/` in the mime types (See examples in [template.yaml](../examples/2016-10-31/implicit_api_settings/template.yaml)). MinimumCompressionSize | `int` | Allow compression of response bodies based on client's Accept-Encoding header. Compression is triggered when response body size is greater than or equal to your configured threshold. The maximum body size threshold is 10 MB (10,485,760 Bytes). The following compression types are supported: gzip, deflate, and identity. Cors | `string` or [Cors Configuration](#cors-configuration) | Enable CORS for all your APIs. Specify the domain to allow as a string or specify a dictionary with additional [Cors Configuration](#cors-configuration). NOTE: Cors requires SAM to modify your OpenAPI definition. Hence it works only inline OpenAPI defined with `DefinitionBody`. @@ -424,6 +424,7 @@ Properties: - [S3](#s3) - [SNS](#sns) - [Kinesis](#kinesis) + - [MSK](#msk) - [DynamoDB](#dynamodb) - [SQS](#sqs) - [Api](#api) @@ -533,6 +534,31 @@ Properties: Destination: !GetAtt MySqsQueue.Arn ``` + +#### MSK + +The object describing an event source with type `MSK`. + +##### Properties + +Property Name | Type | Description +---|:---:|--- +Stream | `string` | **Required.** ARN of the Amazon MSK stream. +StartingPosition | `string` | **Required.** One of `TRIM_HORIZON` or `LATEST`. +Topics | `list` | **Required.** List of Topics created in the Amazon MSK Stream + +##### Example: MSK event source object + +```yaml +Type: Kinesis +Properties: + Stream: arn:aws:kafka:us-west-2:123456789012:cluster/mycluster/6cc0432b-8618-4f44-bccc-e1fbd8fb7c4d-2 + StartingPosition: LATEST + Topics: + - "Topic1" + - "Topic2" +``` + #### DynamoDB The object describing an event source with type `DynamoDB`. @@ -845,7 +871,7 @@ The object describing the source of events which trigger the function. Property Name | Type | Description ---|:---:|--- -Type | `string` | **Required.** Event type. Event source types include '[S3](#s3), '[SNS](#sns)', '[Kinesis](#kinesis)', '[DynamoDB](#dynamodb)', '[SQS](#sqs)', '[Api](#api)', '[Schedule](#schedule)', '[CloudWatchEvent](#cloudwatchevent)', '[CloudWatchLogs](#cloudwatchlogs)', '[IoTRule](#iotrule)', '[AlexaSkill](#alexaskill)'. For more information about the types, see [Event source types](#event-source-types). +Type | `string` | **Required.** Event type. Event source types include '[S3](#s3), '[SNS](#sns)', '[Kinesis](#kinesis)', '[MSK](#msk)', [DynamoDB](#dynamodb)', '[SQS](#sqs)', '[Api](#api)', '[Schedule](#schedule)', '[CloudWatchEvent](#cloudwatchevent)', '[CloudWatchLogs](#cloudwatchlogs)', '[IoTRule](#iotrule)', '[AlexaSkill](#alexaskill)'. For more information about the types, see [Event source types](#event-source-types). Properties | * | **Required.** Object describing properties of this event mapping. Must conform to the defined `Type`. For more information about all types, see [Event source types](#event-source-types). ##### Example: Event source object @@ -949,6 +975,7 @@ Properties: - [Application Location Object](#application-id-object) - [DeadLetterQueue Object](#deadletterqueue-object) - [Cors Configuration](#cors-configuration) +- [API EndpointConfiguration Object](#api-endpointconfiguration-object) - [API Auth Object](#api-auth-object) - [Function Auth Object](#function-auth-object) - [Function Request Model Object](#function-request-model-object) @@ -1043,6 +1070,14 @@ Cors: > NOTE: API Gateway requires literal values to be a quoted string, so don't forget the additional quotes in the `Allow___` values. ie. "'www.example.com'" is correct whereas "www.example.com" is wrong. +#### API EndpointConfiguration Object + +```yaml +EndpointConfiguration: + Type: PRIVATE # OPTIONAL | Default value is REGIONAL. Accepted values are EDGE, REGIONAL, PRIVATE + VPCEndpointIds: [] # REQUIRED if Type is PRIVATE +``` + #### API Auth Object Configure Auth on APIs.