Skip to content

Commit

Permalink
Merge pull request #2439 from JunaidAnsari80/jdansariaws-feature-s3-c…
Browse files Browse the repository at this point in the history
…loudfront-sap

Jdansariaws feature s3 cloudfront sap
  • Loading branch information
julianwood authored Sep 23, 2024
2 parents 4e2b9d9 + 33c4c2d commit e43db15
Show file tree
Hide file tree
Showing 6 changed files with 348 additions and 0 deletions.
66 changes: 66 additions & 0 deletions s3-cloudfront-spa-tf/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# Amazon S3 hosted website served by a CloudFront distribution restricted by CloudFront Origin Access Control (OAC)
This project serves as a demonstration of how to leverage Amazon CloudFront and Amazon Simple Storage Service (S3) to host and deliver a static website efficiently and securely.

Amazon S3 is an object storage service that provides a highly scalable, durable, and cost-effective way to store and retrieve data, including website files such as HTML, CSS, JavaScript, and other static assets. In this project, S3 is utilized as the origin, which means it hosts the static website content.

Amazon CloudFront, on the other hand, is a content delivery network (CDN) service that caches and distributes the website content from multiple edge locations around the world. By integrating CloudFront with the S3-hosted website, the project showcases how to leverage CloudFront's caching and content distribution capabilities to improve website performance, reduce latency, and enhance the end-user experience.

![Demo Project Solution Architecture Diagram](diagram.PNG)

- Learn more about these patterns at https://serverlessland.com/patterns.
- To learn more about submitting a pattern, read the [publishing guidelines page](https://github.com/aws-samples/serverless-patterns/blob/main/PUBLISHING.md).

Important: this application uses various AWS services and there are costs associated with these services after the Free Tier usage - please see the [AWS Pricing page](https://aws.amazon.com/pricing/) for details. You are responsible for any AWS costs incurred. No warranty is implied in this example.

## Requirements

* [Create an AWS account](https://portal.aws.amazon.com/gp/aws/developer/registration/index.html) if you do not already have one and log in. The IAM user that you use must have sufficient permissions to make necessary AWS service calls and manage AWS resources.
* [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured
* [Git Installed](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git)
* [Terraform Installed](https://www.terraform.io/)

## Deployment Instructions
1. Create a new directory, navigate to that directory in a terminal and clone the GitHub repository:
```
git clone https://github.com/aws-samples/serverless-patterns
```
2. Change directory to the pattern directory:
```
cd s3-cloudfront-spa
```
3. Initilize terraform
```
Run terraform init
```
4. Create terrform plan
```
Run terraform plan
```
5. Create AWS resources
```
Run terraform apply
```
During the prompts:
* Enter yes
6. Copy your front end assests
## Testing
1. Go to AWS Console.
2. Go to your hosting Amazon S3 bucket.
3. Upload your front end content or Copy index.html from "s3-cloudfront-spa" pattern "test" folder for testing.
4. Go to AWS Cloudfront.
5. Go to Distributions.
5. Select your distribution domain and browse it. e.g ***********.cloudfront.net/index.html.
### Removing the resources
1. To destory deployed resources
```
Run terraform destroy
```
----
Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.
----
Binary file added s3-cloudfront-spa-tf/diagram.PNG
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
64 changes: 64 additions & 0 deletions s3-cloudfront-spa-tf/example-pattern.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
{
"title": "AWS CloudFront and Amazon S3 for Single Page Application Hosting",
"description": "This project demonstrates hosting a Single-Page App using AWS CloudFront & S3, where CloudFront serves the SPA from an S3 bucket for static hosting.",
"language": "Terraform",
"level": "200",
"framework": "Terraform",
"introBox": {
"headline": "How it works",
"text": [
"Here's how hosting a static website using Amazon CloudFront and Amazon S3 works:",
"- An Amazon S3 bucket is created and configured for static website hosting",
"- A CloudFront web distribution is created, with the S3 bucket configured as the origin server. CloudFront acts as a content delivery network (CDN) for the website",
"- When a user requests a file from the website, CloudFront first checks if the file is cached at one of its edge locations (data centers) closest to the user. If the file is cached, CloudFront serves it directly to the user.",
"- If the requested file is not cached at an edge location, CloudFront fetches the file from the S3 bucket origin. It then caches the file at the edge location for subsequent requests",
"- For subsequent requests, CloudFront serves the cached files from the nearest edge location to the user, providing low latency and fast content delivery.",
"- When website files are updated in the S3 bucket, CloudFront automatically detects the changes and retrieves the updated files from the origin. Cache invalidation can also be manually triggered to force CloudFront to fetch the latest versions."
]
},
"gitHub": {
"template": {
"repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/s3-cloudfront-spa",
"templateURL": "serverless-patterns/s3-cloudfront-spa",
"projectFolder": "s3-cloudfront-spa",
"templateFile": "template.tf"
}
},
"resources": {
"bullets": [
{
"text": "Amazon S3 bucket to host the website",
"link": "https://docs.aws.amazon.com/AmazonS3/latest/userguide/WebsiteHosting.html"
},
{
"text": "Amazon CloudFront uses Amazon S3 as the origin server to host and serve static websites.",
"link": "https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/getting-started-cloudfront-overview.html"
}
]
},
"deploy": {
"text": [
"<code>terraform init</code>",
"<code>terraform plan</code>",
"<code>terraform apply</code>"
]
},
"testing": {
"text": [
"Refer to README"
]
},
"cleanup": {
"text": [
"<code>terraform destroy</code>"
]
},
"authors": [
{
"name": "Junaid Ansari",
"image": "https://media.licdn.com/dms/image/v2/C4E03AQEekYWyjMINfg/profile-displayphoto-shrink_200_200/profile-displayphoto-shrink_200_200/0/1659715539214?e=1728518400&v=beta&t=HVpwkCvJ_QdUYDW2YXbCabZDcWKWv3VpTBKmnCS5uVg",
"bio": "Cloud Application Architect at AWS.",
"linkedin": "junaid-ansari-2734a912"
}
]
}
83 changes: 83 additions & 0 deletions s3-cloudfront-spa-tf/s3-cloudfront-spa-tf.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
{
"title": "Amazon CloudFront and Amazon S3 for Single Page Application Hosting",
"description": "This project hosts a Single-Page App using Amazon CloudFront & S3, where CloudFront serves the SPA from an S3 bucket for static hosting.",
"language": "YAML",
"level": "200",
"framework": "Terraform",
"introBox": {
"headline": "How it works",
"text": [
"Here's how hosting a static website using Amazon CloudFront and Amazon S3 works:",
"- An Amazon S3 bucket is created and configured for static website hosting",
"- A CloudFront web distribution is created, with the S3 bucket configured as the origin server. CloudFront acts as a content delivery network (CDN) for the website",
"- When a user requests a file from the website, CloudFront first checks if the file is cached at one of its edge locations (data centers) closest to the user. If the file is cached, CloudFront serves it directly to the user.",
"- If the requested file is not cached at an edge location, CloudFront fetches the file from the S3 bucket origin. It then caches the file at the edge location for subsequent requests",
"- For subsequent requests, CloudFront serves the cached files from the nearest edge location to the user, providing low latency and fast content delivery.",
"- When website files are updated in the S3 bucket, CloudFront automatically detects the changes and retrieves the updated files from the origin. Cache invalidation can also be manually triggered to force CloudFront to fetch the latest versions."
]
},
"gitHub": {
"template": {
"repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/s3-cloudfront-spa",
"templateURL": "serverless-patterns/s3-cloudfront-spa",
"projectFolder": "s3-cloudfront-spa",
"templateFile": "template.tf"
}
},
"resources": {
"bullets": [
{
"text": "Amazon S3 bucket to host the website",
"link": "https://docs.aws.amazon.com/AmazonS3/latest/userguide/WebsiteHosting.html"
},
{
"text": "Amazon CloudFront uses Amazon S3 as the origin server to host and serve static websites.",
"link": "https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/getting-started-cloudfront-overview.html"
}
]
},
"deploy": {
"text": [
"<code>terraform init</code>",
"<code>terraform plan</code>",
"<code>terraform apply</code>"
]
},
"testing": {
"text": [
"Refer to README"
]
},
"cleanup": {
"text": [
"<code>terraform destroy</code>"
]
},
"authors": [
{
"name": "Junaid Ansari",
"image": "https://media.licdn.com/dms/image/v2/C4E03AQEekYWyjMINfg/profile-displayphoto-shrink_200_200/profile-displayphoto-shrink_200_200/0/1659715539214?e=1728518400&v=beta&t=HVpwkCvJ_QdUYDW2YXbCabZDcWKWv3VpTBKmnCS5uVg",
"bio": "Cloud Application Architect at AWS.",
"linkedin": "junaid-ansari-2734a912"
}
],
"patternArch": {
"icon1": {
"x": 20,
"y": 50,
"service": "cloudfront-edge",
"label": "Amazon CloudFront distribution"
},
"icon2": {
"x": 80,
"y": 50,
"service": "s3",
"label": "Amazon S3 bucket"
},
"line1": {
"from": "icon1",
"to": "icon2",
"label": ""
}
}
}
134 changes: 134 additions & 0 deletions s3-cloudfront-spa-tf/terraform/template.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.61"
}
}
}

provider "aws" {
region = "eu-west-2"
profile = "default"
}

data "aws_region" "current" {
}

data "aws_caller_identity" "current" {
}

resource "aws_cloudfront_origin_access_control" "spa_aoc" {
name = "spa_aoc"
description = "spa_aoc"
origin_access_control_origin_type = "s3"
signing_behavior = "always"
signing_protocol = "sigv4"
}

resource "aws_cloudfront_distribution" "cf_distribution" {
enabled = true
default_root_object = "index.html"
is_ipv6_enabled = true
price_class = "PriceClass_All"

restrictions {
geo_restriction {
restriction_type = "none"
}
}
origin {
domain_name = aws_s3_bucket.host.bucket_regional_domain_name
origin_id = "spa_portal"
origin_access_control_id = aws_cloudfront_origin_access_control.spa_aoc.id
}

default_cache_behavior {
allowed_methods = ["DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT"]
cached_methods = ["GET", "HEAD"]
target_origin_id = "spa_portal"

forwarded_values {
query_string = false

cookies {
forward = "none"
}
}

viewer_protocol_policy = "redirect-to-https"
min_ttl = 0
default_ttl = 300
max_ttl = 1200
}

viewer_certificate {
cloudfront_default_certificate = true
minimum_protocol_version = "TLSv1.2_2021"
}
}

locals {
s3_origin_id = "host-store"
}

resource "aws_s3_bucket" "host" {
bucket = local.s3_origin_id
}

resource "aws_s3_bucket_public_access_block" "bucket_access_block" {
bucket = aws_s3_bucket.host.id
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}

resource "aws_s3_bucket_ownership_controls" "host_ownership" {
bucket = aws_s3_bucket.host.id
rule {
object_ownership = "BucketOwnerPreferred"
}
}

resource "aws_s3_bucket_acl" "host_acl" {
depends_on = [aws_s3_bucket_ownership_controls.host_ownership]

bucket = aws_s3_bucket.host.id
acl = "private"
}


resource "aws_s3_bucket_website_configuration" "website_configuration" {
bucket = aws_s3_bucket.host.id

index_document {
suffix = "index.html"
}

error_document {
key = "error.html"
}
}

resource "aws_s3_bucket_policy" "host_policy" {
bucket = aws_s3_bucket.host.id

policy = jsonencode({
Statement = [
{
Action = "s3:GetObject"
Effect = "Allow"
Resource = "arn:aws:s3:::${aws_s3_bucket.host.bucket}/*"
Principal = {
Service = "cloudfront.amazonaws.com"
},
Condition = {
StringEquals = {
"AWS:SourceArn" = "arn:aws:cloudfront::${data.aws_caller_identity.current.account_id}:distribution/${aws_cloudfront_distribution.cf_distribution.id}"
}
}
}
]
})
}
1 change: 1 addition & 0 deletions s3-cloudfront-spa-tf/test/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<p>Welcome to SPA</p>

0 comments on commit e43db15

Please sign in to comment.