Create a serverless web application using S3, CloudFront, API Gateway, Lambda, DynamoDB and Cognito.
AWSTemplateFormatVersion: "2010-09-09"
Transform: AWS::Serverless-2016-10-31
Description: >
(uksb-1tthgi812) (tag:sam-webapp-cognito)
Serverless WebApp with S3, CloudFront, API Gateway, DynamoDB and Cognito.
Resources:
# Origin Access Control to restrict access to S3 bucket by sending authenticated requests to S3.
CloudFrontOriginAccessControl:
Type: AWS::CloudFront::OriginAccessControl
Properties:
OriginAccessControlConfig:
Description: S3 Origin Access Control
Name: S3OriginAccessControl
OriginAccessControlOriginType: s3
SigningBehavior: no-override
SigningProtocol: sigv4
# CloudFront cache policy to cache contents for 3 hours.
CachePolicy:
Type: AWS::CloudFront::CachePolicy
Properties:
CachePolicyConfig:
Comment: Cache for 3hrs
Name: !Ref AWS::StackName
DefaultTTL: 10800
MaxTTL: 10800
MinTTL: 10800
Name: 3h
ParametersInCacheKeyAndForwardedToOrigin:
CookiesConfig:
CookieBehavior: none
EnableAcceptEncodingBrotli: false
EnableAcceptEncodingGzip: false
HeadersConfig:
HeaderBehavior: whitelist
Headers:
- x-forwarded-for
QueryStringsConfig:
QueryStringBehavior: whitelist
QueryStrings:
- allowed_query_string_param
CloudfrontDistribution:
Type: "AWS::CloudFront::Distribution"
Properties:
DistributionConfig:
Comment: "Cloudfront distribution for serverless webapp"
DefaultRootObject: "index.html"
Enabled: true
HttpVersion: http2
# List of origins that Cloudfront will connect to
Origins:
# Origin for static assests in S3
- Id: s3-website
DomainName: !GetAtt FrontEndS3Bucket.DomainName
S3OriginConfig:
OriginAccessIdentity:
Fn::Sub: ""
# Restricting Bucket access through origin access control
OriginAccessControlId: !GetAtt CloudFrontOriginAccessControl.Id
# Origin for api
- Id: api
DomainName: !Sub "${HttpApi}.execute-api.${AWS::Region}.amazonaws.com"
CustomOriginConfig:
HTTPSPort: 443
OriginProtocolPolicy: https-only
OriginSSLProtocols:
- TLSv1.2
# To connect the CDN to the origins you need to specify behaviours
DefaultCacheBehavior:
# Compress resources automatically ( gzip )
Compress: "true"
AllowedMethods:
- GET
- HEAD
- OPTIONS
ForwardedValues:
QueryString: false
TargetOriginId: s3-website
ViewerProtocolPolicy: redirect-to-https
CachePolicyId: !Ref CachePolicy
CacheBehaviors:
- PathPattern: /api
Compress: "true"
AllowedMethods:
- GET
- HEAD
- OPTIONS
- PUT
- POST
- DELETE
- PATCH
TargetOriginId: api
ViewerProtocolPolicy: redirect-to-https
#AWS managed cache-policy that disables caching. Recommended for API Gateway Origins.
CachePolicyId: 4135ea2d-6df8-44a3-9df3-4b5a84be39ad
# S3 bucket to host static content.
FrontEndS3Bucket:
Type: AWS::S3::Bucket
# Bucket policy to restrict access only to CloudFront
S3BucketPolicy:
Type: AWS::S3::BucketPolicy
Properties:
Bucket: !Ref FrontEndS3Bucket
PolicyDocument:
# Restricting access to cloudfront only.
Statement:
- Effect: Allow
Action: "s3:GetObject"
Resource:
- !Sub "arn:aws:s3:::${FrontEndS3Bucket}/*"
Principal:
Service: "cloudfront.amazonaws.com"
Condition:
StringEquals:
AWS:SourceArn: !Sub "arn:aws:cloudfront::${AWS::AccountId}:distribution/${CloudfrontDistribution}"
# Cognito User Pool
UserPool:
Type: AWS::Cognito::UserPool
Properties:
UserPoolName: WildRydes
AliasAttributes:
- email
AutoVerifiedAttributes:
- email
# Cognito client
UserPoolClient:
Type: AWS::Cognito::UserPoolClient
Properties:
ClientName: WildRydesWeb
UserPoolId: !Ref UserPool
GenerateSecret: false
DynamoTable:
Type: AWS::DynamoDB::Table
Properties:
TableName: Rides
AttributeDefinitions:
- AttributeName: Id
AttributeType: S
KeySchema:
- AttributeName: Id
KeyType: HASH
BillingMode: PAY_PER_REQUEST
# HTTP API with CORS configuration
HttpApi:
Type: AWS::Serverless::HttpApi
Properties:
Description: Serverless webapp api
FailOnWarnings: true
CorsConfiguration:
AllowMethods:
- GET
- HEAD
- OPTIONS
AllowOrigins:
- "*"
AllowHeaders:
- "*"
Auth:
Authorizers:
OAuth2Authorizer:
IdentitySource: "$request.header.Authorization"
JwtConfiguration:
issuer: !Sub https://cognito-idp.${AWS::Region}.amazonaws.com/${UserPool}
audience:
- !Ref UserPoolClient
DefaultAuthorizer: OAuth2Authorizer
# Lambda function with API Gateway as event source and access to DynamoDB table.
LamdaFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: src/
Handler: app.handler
Runtime: nodejs20.x
Events:
HttpApi:
Type: HttpApi
Properties:
ApiId: !Ref HttpApi
Path: /api
Method: POST
Policies:
## Read more about SAM Policy templates at:
## https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-policy-templates.html
- DynamoDBWritePolicy:
TableName: !Ref DynamoTable
Outputs:
WebAppDomain:
Value: !GetAtt CloudfrontDistribution.DomainName
CloudFrontDistributionId:
Value: !GetAtt CloudfrontDistribution.Id
WebAppS3BucketName:
Value: !Ref FrontEndS3Bucket
CognitoUserPoolId:
Value: !Ref UserPool
Export:
Name: "UserPool::Id"
CognitoUserPoolClientId:
Value: !Ref UserPoolClient
Export:
Name: "UserPoolClient::Id"
Issuer:
Description: "Url used for issuer on HTTP API JWT tokens"
Value: !Sub https://cognito-idp.${AWS::Region}.amazonaws.com/${UserPool}
DynamoDbTable:
Value: !Ref DynamoTable
Description: DynamoDb Table
HttpApiEndpoint:
Description: API Endpoint
Value: !Sub "https://${HttpApi}.execute-api.${AWS::Region}.amazonaws.com"
git clone https://github.com/aws-samples/serverless-patterns
cd serverless-patterns/sam-webapp-cognito