diff --git a/.gitignore b/.gitignore index 5f18ba3..bbde8c9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,2 @@ params*json -!params-example.json \ No newline at end of file +!params-example.json diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d3255e7 --- /dev/null +++ b/LICENSE @@ -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. \ No newline at end of file diff --git a/README b/README new file mode 100644 index 0000000..518075e --- /dev/null +++ b/README @@ -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 --template-body file://template.yml --parameters file://params.json + +To update existing stack: +aws cloudformation create-stack --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 --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/). diff --git a/params-example.json b/params-example.json new file mode 100644 index 0000000..b88f4ad --- /dev/null +++ b/params-example.json @@ -0,0 +1,22 @@ +[ + { + "ParameterKey": "AppEnvironment", + "ParameterValue": "env" + }, + { + "ParameterKey": "AppName", + "ParameterValue": "app-name" + }, + { + "ParameterKey": "CertificateArn", + "ParameterValue": "arn:aws:acm:::certificate/" + }, + { + "ParameterKey": "ZoneName", + "ParameterValue": "" + }, + { + "ParameterKey": "ZoneSubdomain", + "ParameterValue": "" + } +] diff --git a/template.yml b/template.yml new file mode 100644 index 0000000..1458d10 --- /dev/null +++ b/template.yml @@ -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