Skip to content

Added template, README, parameter example file and LICENSE #1

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
params*json
!params-example.json
!params-example.json
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2024 QR Code Generator Inc.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
80 changes: 80 additions & 0 deletions README
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# Hosting single page applications on cloudfront with s3 object storage

CloudFormation template for creating the AWS components for SPA hosting on Cloudfront+S3 with Route53 records for subdomain creation. After the creation of the stack, a pipeline needs to be created to build and deploy the code to the s3 bucket specified in the stack output.

## Usage

#### Prerequisites:

1. [Install](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html) AWS CLI.

2. [Configure](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-files.html) a profile in the aws cli.

#### Template Deployment:

The cloudformation template can be deployed directly using cli. The code deployment can be setup using a codepipeline to deploy to the S3 bucket.

```
To create new stack:
aws cloudformation create-stack --stack-name <stack-name> --template-body file://template.yml --parameters file://params.json

To update existing stack:
aws cloudformation create-stack --stack-name <stack-name> --template-body file://template.yml --parameters file://params.json

To observe stack status:
watch -g -n 5 aws cloudformation describe-stacks --stack-name <stack-name> --query 'Stacks[0].StackStatus'
```

#### Available Parameters

Following parameters are available for customization. Defaults can be set in the template, and can be overridden via the params.json(refer params-example.json for structure). The cache, response headers and origin request policies have been set according to S3 origin, the policy id can be overridden to point to custom user created policies.

```yaml
AppEnvironment:
Type: String
Description: The environment this stack should be assumed as.
Default: staging
AllowedValues:
- staging
- production
AppName:
Type: String
Description: Project name or app name.
CertificateArn:
Type: String
Description: ARN of the SSL certificate to use for HTTPS listener.
CloudFrontCachePolicy:
Type: String
Description: Managed-CachingOptimized policy ID
Default: 658327ea-f89d-4fab-a63d-7e88639e58f6
CloudFrontResponseHeadersPolicy:
Type: String
Description: Managed-CORS-With-Preflight response headerspolicy ID.
Default: 5cc3b908-e619-4b99-88e5-2cf7f45965bd
CloudFrontOriginRequestPolicyId:
Type: String
Description: Managed-CORS-S3Origin origin request policy ID.
Default: 88a5eaf4-2fd4-4709-b370-b4c650ea3fcf
CustomErrorResponsePath:
Type: String
Description: Custom error page response path.
Default: "/index.html"
DefaultRootObject:
Type: String
Description: The object that you want CloudFront to request from your origin (for example, index.html)
Default: "index.html"
ErrorCachingMinTTL:
Type: Number
Description: Mininum TTL for error caching
Default: 10
ZoneName:
Type: String
Description: The Route 53 zone under which subdomain(s) will be created.
ZoneSubdomain:
Type: String
Description: The subdomain which will point to the load balancer in this stack.
```

## License

See the [LICENSE](LICENSE) file for details. Made with ❤️ at [QRStuff](https://qrstuff.com/).
22 changes: 22 additions & 0 deletions params-example.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
[
{
"ParameterKey": "AppEnvironment",
"ParameterValue": "env"
},
{
"ParameterKey": "AppName",
"ParameterValue": "app-name"
},
{
"ParameterKey": "CertificateArn",
"ParameterValue": "arn:aws:acm:<region>:<account-id>:certificate/<certificate-id>"
},
{
"ParameterKey": "ZoneName",
"ParameterValue": "<zone-name>"
},
{
"ParameterKey": "ZoneSubdomain",
"ParameterValue": "<zone-subdomain-name>"
}
]
135 changes: 135 additions & 0 deletions template.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
AWSTemplateFormatVersion: 2010-09-09
Description: Cloudfront+S3 deployment stack.

Parameters:
AppEnvironment:
Type: String
Description: The environment this stack should be assumed as.
Default: staging
AllowedValues:
- staging
- production
AppName:
Type: String
Description: Project name or app name.
CertificateArn:
Type: String
Description: ARN of the SSL certificate to use for HTTPS listener.
CloudFrontCachePolicy:
Type: String
Description: Managed-CachingOptimized policy ID
Default: 658327ea-f89d-4fab-a63d-7e88639e58f6
CloudFrontResponseHeadersPolicy:
Type: String
Description: Managed-CORS-With-Preflight response headerspolicy ID.
Default: 5cc3b908-e619-4b99-88e5-2cf7f45965bd
CloudFrontOriginRequestPolicyId:
Type: String
Description: Managed-CORS-S3Origin origin request policy ID.
Default: 88a5eaf4-2fd4-4709-b370-b4c650ea3fcf
CustomErrorResponsePath:
Type: String
Description: Custom error page response path.
Default: "/index.html"
DefaultRootObject:
Type: String
Description: The object that you want CloudFront to request from your origin (for example, index.html)
Default: "index.html"
ErrorCachingMinTTL:
Type: Number
Description: Mininum TTL for error caching
Default: 10
ZoneName:
Type: String
Description: The Route 53 zone under which subdomain(s) will be created.
ZoneSubdomain:
Type: String
Description: The subdomain which will point to the load balancer in this stack.

Resources:
S3Bucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Join ["-", [!Ref AppName, !Ref AppEnvironment]]

CloudFrontOriginIdentity:
Type: AWS::CloudFront::CloudFrontOriginAccessIdentity
Properties:
CloudFrontOriginAccessIdentityConfig:
Comment: !Sub "CloudFront OAI for ${AppName} ${AppEnvironment}"

S3BucketPolicy:
Type: AWS::S3::BucketPolicy
Properties:
Bucket: !Ref S3Bucket
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
CanonicalUser: !GetAtt CloudFrontOriginIdentity.S3CanonicalUserId
Action: s3:GetObject
Resource: !Sub "${S3Bucket.Arn}/*"

CloudFrontDistribution:
Type: AWS::CloudFront::Distribution
Properties:
DistributionConfig:
Aliases:
- !Join ["", [!Ref ZoneSubdomain, ., !Ref ZoneName]]
CustomErrorResponses:
- ErrorCachingMinTTL: !Ref ErrorCachingMinTTL
ErrorCode: "403"
ResponseCode: "200"
ResponsePagePath: !Ref CustomErrorResponsePath
- ErrorCachingMinTTL: !Ref ErrorCachingMinTTL
ErrorCode: "404"
ResponseCode: "200"
ResponsePagePath: !Ref CustomErrorResponsePath
DefaultCacheBehavior:
AllowedMethods:
- GET
- HEAD
- OPTIONS
CachePolicyId: !Ref CloudFrontCachePolicy
ForwardedValues:
QueryString: true
OriginRequestPolicyId: !Ref CloudFrontOriginRequestPolicyId
ResponseHeadersPolicyId: !Ref CloudFrontResponseHeadersPolicy
TargetOriginId: !GetAtt S3Bucket.RegionalDomainName
ViewerProtocolPolicy: redirect-to-https
DefaultRootObject: !Ref DefaultRootObject
Enabled: true
HttpVersion: http2
Origins:
- Id: !GetAtt S3Bucket.RegionalDomainName
DomainName: !GetAtt S3Bucket.RegionalDomainName
OriginPath: ""
S3OriginConfig:
OriginAccessIdentity: !Sub "origin-access-identity/cloudfront/${CloudFrontOriginIdentity}"
ConnectionAttempts: 3
ConnectionTimeout: 10
PriceClass: PriceClass_All
ViewerCertificate:
AcmCertificateArn: !Ref CertificateArn
MinimumProtocolVersion: TLSv1.2_2021
SslSupportMethod: sni-only

CloudFrontDistributionDNSRecord:
Type: AWS::Route53::RecordSet
Properties:
HostedZoneName: !Join ["", [!Ref ZoneName, .]]
Name: !Join ["", [!Ref ZoneSubdomain, ., !Ref ZoneName, .]]
Type: CNAME
TTL: "3600"
ResourceRecords:
- !GetAtt CloudFrontDistribution.DomainName

Outputs:
CloudfrontEndpoint:
Description: Assets CDN endpoint
Value: !Join ["", ["https://", !Ref CloudFrontDistributionDNSRecord]]

S3BucketName:
Description: Name of the bucket to deploy code to. The base directory should contain the document root for the project.
Value: !Ref S3Bucket