Skip to content

Instantly share code, notes, and snippets.

@chris
Last active December 17, 2021 05:14
Show Gist options
  • Star 7 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save chris/2540ff9357cd69980b13a0b3f979c9ed to your computer and use it in GitHub Desktop.
Save chris/2540ff9357cd69980b13a0b3f979c9ed to your computer and use it in GitHub Desktop.
Serverless Framework config file for creating API Gateway to DynamoDB proxy
service: my-api
org: CompanyName
# You can pin your service to only deploy with a specific Serverless version
# Check out our docs for more details
# frameworkVersion: "=X.X.X"
frameworkVersion: '>=2.1.1 <3.0.0'
custom:
defaultStage: dev
profiles:
dev: companyname-dev
production: companyname-prod
dynamodb:
stages:
- dev
start:
inMemory: true
migrate: true
provider:
name: aws
runtime: go1.x
region: us-west-1
stage: ${opt:stage, self:custom.defaultStage}
profile: ${self:custom.profiles.${self:provider.stage}}
# Uncomment this to turn on API Gateway logs
# logs:
# restApi: true
tracing:
lambda: true
apiGateway: true
plugins:
- serverless-plugin-scripts
- serverless-dynamodb-local
package:
exclude:
- ./**
include:
- ./bin/**
functions:
resources:
Resources:
#
# Cognito setup
#
YourProductUserPool:
Type: AWS::Cognito::UserPool
Properties:
UserPoolName: yourproduct_user_pool
UsernameAttributes: # use email as username/login
- 'email'
AutoVerifiedAttributes:
- 'email'
Policies:
PasswordPolicy:
MinimumLength: 8
RequireLowercase: true
RequireNumbers: true
RequireSymbols: false
RequireUppercase: true
AccountRecoverySetting:
RecoveryMechanisms:
- Name: verified_email
Priority: 1
VerificationMessageTemplate:
DefaultEmailOption: CONFIRM_WITH_CODE
EmailMessage: 'Thanks for signing up with YourProduct! Your verification code is {####}'
EmailSubject: 'Your YourProduct verification code'
UserPoolTags:
YourProduct: 'true'
YourProductCognitoClient:
Type: AWS::Cognito::UserPoolClient
Properties:
ClientName: yourproduct_user_pool_client
UserPoolId:
Ref: YourProductUserPool
YourProductIdentityPool:
Type: AWS::Cognito::IdentityPool
Properties:
IdentityPoolName: yourproduct_identity_pool
AllowUnauthenticatedIdentities: false
CognitoIdentityProviders:
- ClientId:
Ref: YourProductCognitoClient
ProviderName:
Fn::GetAtt: [YourProductUserPool, ProviderName]
YourProductIdentityPoolRoles:
Type: AWS::Cognito::IdentityPoolRoleAttachment
Properties:
IdentityPoolId:
Ref: YourProductIdentityPool
Roles:
authenticated:
Fn::GetAtt: [YourProductUserAuthRole, Arn]
unauthenticated:
Fn::GetAtt: [YourProductUserUnauthRole, Arn]
YourProductUserAuthRole:
Type: AWS::IAM::Role
Properties:
RoleName: yourProductUserAuthRole
Path: /
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: 'Allow'
Principal:
Federated: 'cognito-identity.amazonaws.com'
Action:
- 'sts:AssumeRoleWithWebIdentity'
Condition:
StringEquals:
'cognito-identity.amazonaws.com:aud':
Ref: YourProductIdentityPool
'ForAnyValue:StringLike':
'cognito-identity.amazonaws.com:amr': authenticated
Policies:
- PolicyName: 'YourProductUserAuthorizedPolicy'
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: 'Allow'
Action:
- 'mobileanalytics:PutEvents'
- 'cognito-sync:*'
- 'cognito-identity:*'
Resource: '*'
- Effect: 'Allow'
Action:
- 'execute-api:Invoke'
Resource: '*'
YourProductUserUnauthRole:
Type: AWS::IAM::Role
Properties:
RoleName: yourProductUserUnauthRole
Path: /
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: 'Allow'
Principal:
Federated: 'cognito-identity.amazonaws.com'
Action:
- 'sts:AssumeRoleWithWebIdentity'
Condition:
StringEquals:
'cognito-identity.amazonaws.com:aud':
Ref: YourProductIdentityPool
'ForAnyValue:StringLike':
'cognito-identity.amazonaws.com:amr': unauthenticated
Policies:
- PolicyName: 'YourProductUserUnauthorizedPolicy'
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: 'Allow'
Action:
- 'mobileanalytics:PutEvents'
- 'cognito-sync:*'
- 'cognito-identity:*'
Resource: '*'
#
# DynamoDB events table
#
EventsTable:
Type: AWS::DynamoDB::Table
Properties:
TableName: yourproduct_events
AttributeDefinitions:
- AttributeName: UserID
AttributeType: S
- AttributeName: TimeUTC
AttributeType: S
KeySchema:
- AttributeName: UserID
KeyType: HASH
- AttributeName: TimeUTC
KeyType: RANGE
BillingMode: PAY_PER_REQUEST
Tags:
- Key: 'YourProduct'
Value: 'true'
#
# API Gateway + VTL template to put events into above DynamoDB table
#
YourProductApiGatewayDynamoRole:
Type: AWS::IAM::Role
Properties:
RoleName: yourProductApiGatewayDynamoRole
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: 'Allow'
Principal:
Service: 'apigateway.amazonaws.com'
Action:
- 'sts:AssumeRole'
Policies:
- PolicyName: 'YourProductApiGatewayDynamoPolicy'
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: 'Allow'
Action:
- 'dynamodb:DescribeTable'
- 'dynamodb:BatchWriteItem'
- 'dynamodb:PutItem'
- 'dynamodb:Query'
- 'dynamodb:UpdateItem'
Resource:
- Fn::GetAtt: [EventsTable, Arn]
- Effect: 'Allow'
Action:
- 'dynamodb:ListTables'
Resource: '*'
Condition: {}
# This is a Cognito User Pool authorizer that allows API calls to NatureDose
# API endpoints are auth'ed via the Authorization header, without needing to
# do AWS v4 signing. This is unnecessary if using v4 signing, where you can
# simply set the lambda function to have `authorization: aws_iam`. See:
# https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-enable-cognito-user-pool.html
# To set it to be used with a given lambda/API, add an "authorizer" section to
# the lambda such as:
# authorizer:
# type: COGNITO_USER_POOLS
# authorizerId:
# Ref: NatureDoseAPIAuthorizer
YourProductAPIAuthorizer:
Type: AWS::ApiGateway::Authorizer
Properties:
# AuthorizerCredentials: String
AuthorizerResultTtlInSeconds: 300
# AuthorizerUri: String
AuthType: String
IdentitySource: method.request.header.Authorization
# IdentityValidationExpression: String
Name: YourProductAPIAuthorizer
ProviderARNs:
- Fn::GetAtt: [YourProductUserPool, Arn]
RestApiId:
Ref: ApiGatewayRestApi
Type: COGNITO_USER_POOLS
# The resource basically defines the URL path the API is attached to
EventsResource:
Type: AWS::ApiGateway::Resource
Properties:
RestApiId:
Ref: ApiGatewayRestApi
ParentId:
Fn::GetAtt:
- ApiGatewayRestApi
- RootResourceId
PathPart: events
EventsDeployment:
Type: AWS::ApiGateway::Deployment
Properties:
RestApiId:
Ref: ApiGatewayRestApi
StageName: ${self:provider.stage}
EventsAPI:
Type: AWS::ApiGateway::Method
Properties:
RestApiId:
Ref: ApiGatewayRestApi
ResourceId:
Ref: EventsResource
OperationName: 'AddEvents'
AuthorizerId:
Ref: YourProductAPIAuthorizer
AuthorizationType: COGNITO_USER_POOLS
HttpMethod: POST
Integration:
Type: AWS
IntegrationHttpMethod: POST
Uri: arn:aws:apigateway:us-east-2:dynamodb:action/BatchWriteItem
Credentials:
Fn::GetAtt: [YourProductApiGatewayDynamoRole, Arn]
PassthroughBehavior: NEVER
RequestTemplates:
application/json: |
#set($inputRoot = $input.path('$'))
{
"RequestItems": {
"${self:resources.Resources.EventsTable.Properties.TableName}": [
#foreach($event in $inputRoot.event)
{
"PutRequest": {
"Item": {
"UserID" : { "S": "$context.authorizer.claims.sub" },
"TimeUTC" : { "S": "$event.time" },
"Lat": { "N": "$event.lat" },
"Lng": { "N": "$event.lng" },
"UUID" : { "S": "$event.uuid"},
"Sensor" : { "S": "$event.sensor_name" },
"Reading" : { "N": "$event.reading_value" },
"Active" : { "BOOL": "$event.is_active" },
}
}
}#if($foreach.hasNext),#end
#end
]
},
"ReturnConsumedCapacity": "NONE",
"ReturnItemCollectionMetrics": "NONE"
}
IntegrationResponses:
- StatusCode: '200'
SelectionPattern: "2\\d{2}"
ResponseParameters:
method.response.header.Access-Control-Allow-Origin: "'*'"
ResponseTemplates:
application/json: '{}'
- StatusCode: '400'
SelectionPattern: "4\\d{2}"
ResponseParameters:
method.response.header.Access-Control-Allow-Origin: "'*'"
MethodResponses:
- StatusCode: '200'
ResponseParameters:
method.response.header.Access-Control-Allow-Origin: true
ResponseModels:
application/json: Empty
- StatusCode: '400'
ResponseParameters:
method.response.header.Access-Control-Allow-Origin: true
ResponseModels:
application/json: Error
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment