Imagine you've just come up with an incredible idea for a project. You're excited, you're motivated, and you can't wait to share it with the world. But there's a catch - your budget is tight.
Don't worry, we've got you covered! You can create a stunning static website to showcase your idea without breaking the bank.
In today's digital landscape, Wordpress often takes the spotlight with its extensive features and plugins. However, it relies heavily on PHP to generate content on the fly, which can slow down your page load times. Plus, you'll need a webserver to host it, which can be a bit pricey.
Enter the world of static website hosting. With a static website, your content is
ready to go as soon as your page is requested, leading to faster load times and a
smoother user experience.
And the best part? You can host your static website on an S3 bucket AWS, an
affordable object storage solution that can hold your HTML, CSS, JS files, and even
media assets like images and videos. Combine your S3 bucket AWS with Cloudfront, a
top-notch CDN, and you've got the perfect setup for your static website.
Setting up your infrastructure might sound daunting, but it's actually quite straightforward. All you need is Cloudfront and an S3 bucket AWS. To make things even easier, we'll automate the infrastructure deployment using Terraform. We'll also implement basic security checks on the Terraform code with Trivy as part of a pre-commit set of hooks. This way, you can focus on bringing your awesome idea to life while we handle the technicalities.
The proposed setup is this one:
If you don't have installed Terraform on your machin follow this excellent tutorial provided by Hashicorp. Once you have it up and running you can follow with the next step
First things first! Lets get the AWS access key for your user following the
instructions in
this page.
Keep those keys PRIVATE. Don't upload it to git or other public
repositories where it could be exposed beucase that would allow others to access
your account.
If that happens please regenerate the tokens immediately!
Ok, let's write our first file versions.tf. Here we will define the version of terraform and AWS providers we're going to use:
terraform {
required_version = "~>1.8.3"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.50"
}
}
}
Here we tell Terraform to use the version 1.8.3 for Terraform, and 5.50 for AWS provider
The next step is to write variables.tf file. This block of code will allow us to adapt the project to our needs:
variable "aws_access_key" {
type = string
description = "AWS access key"
sensitive = true
}
variable "aws_secret_key" {
type = string
description = "AWS secret key"
sensitive = true
}
variable "aws_region" {
type = string
description = "AWS region"
default = "us-east-1"
}
variable "stack" {
type = string
description = "defines the stack to deploy"
}
The last step to connect Terraform to AWS is to configure the AWS provider using the code written previously. Let's write the providers.tf
provider "aws" {
access_key = var.aws_access_key
secret_key = var.aws_secret_key
region = var.aws_region
default_tags {
tags = {
Stack = var.stack
}
}
}
This is the minimum required code to make our installation work and connect to AWS.
If everything is correct you should be able to initialize your terraform project and do a first plan by running the command:
terraform init
It should show you something like:
This part is a little bit longer because it requires the creation of several resources:
#S3 encryption is not required due the data on this bucket is publicly accessible
# tfsec:ignore:avd-aws-0088 tfsec:ignore:avd-aws-0089 tfsec:ignore:avd-aws-0090 tfsec:ignore:avd-aws-0132 tfsec:ignore:avd-aws-0320
resource "aws_s3_bucket" "website_bucket" {
bucket = local.bucket_name
}
resource "aws_s3_bucket_public_access_block" "website_bucket_acl" {
bucket = aws_s3_bucket.website_bucket.id
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}
resource "aws_s3_bucket_website_configuration" "website_bucket_website_configuration" {
bucket = aws_s3_bucket.website_bucket.bucket
index_document {
suffix = "index.html"
}
error_document {
key = "index.html"
}
}
data "aws_iam_policy_document" "website_bucket_policy_document" {
statement {
sid = "AllowCloudFrontServicePrincipal"
effect = "Allow"
principals {
type = "Service"
identifiers = ["cloudfront.amazonaws.com"]
}
actions = ["s3:GetObject"]
resources = [aws_s3_bucket.website_bucket.arn, "${aws_s3_bucket.website_bucket.arn}/*"]
condition {
test = "StringEquals"
variable = "AWS:SourceArn"
values = [aws_cloudfront_distribution.this.arn]
}
}
statement {
sid = "AllowCloudFrontOriginAccessIdentity"
effect = "Allow"
principals {
type = "AWS"
identifiers = [aws_cloudfront_origin_access_identity.origin_access_identity.iam_arn]
}
actions = ["s3:GetObject"]
resources = [aws_s3_bucket.website_bucket.arn, "${aws_s3_bucket.website_bucket.arn}/*"]
}
}
resource "aws_s3_bucket_policy" "website_bucket_policy" {
bucket = aws_s3_bucket.website_bucket.id
policy = data.aws_iam_policy_document.website_bucket_policy_document.json
}
/*UPLOADING EXAMPLE WEBSITE*/
# this is not the ideal way to deploy code into S3. This was created to show how the static site will work
resource "aws_s3_object" "dist" {
for_each = fileset("./example-website-code", "**")
bucket = aws_s3_bucket.website_bucket.id
key = each.value
source = "./example-website-code/${each.value}"
content_type = lookup(local.content_type_map, reverse(split(".", "./example-website-code/${each.value}"))[0], "text/html")
# update files when they changes
etag = filemd5("./example-website-code/${each.value}")
}
The file
locals.tf
has other variables that won't be changed to often.
It defines the bucket name and creates a map that is used by
aws_s3_object to set the right content content_type:
locals {
content_type_map = {
"js" = "application/json"
"html" = "text/html"
"css" = "text/css"
}
bucket_name = "my-site.stoneitcloud.com"
}
The functioning of the map is self explainatory. When terraform finds a file ending on js it assigns the content type application/json, when it finds a file with extension html it assings the content type text/html, and for css files it assings the content type text/css
You can add other extensions and content type declaration to cover the case of images, videos and other assets.
AWS CloudFront is a Content Delivery Network (CDN) service provided by Amazon Web Services. A CDN is a globally distributed network of servers that helps deliver content to users with high performance and low latency.
When a user requests content from a website or application, the CDN serves the content from the server that is geographically closest to the user. This reduces the distance the content needs to travel, resulting in faster load times and improved user experience.
CDNs like AWS CloudFront work by caching content in multiple edge locations around the world. These edge locations act as points of presence (PoPs) and store copies of the content. When a user requests the content, the CDN routes the request to the nearest edge location, which then delivers the cached content to the user.
By using AWS CloudFront, content providers can ensure that their content is delivered quickly and reliably to users around the world, regardless of their geographical location.
Let's create the Cloudfront distribution by creating the file cloudfront.tf:
resource "aws_cloudfront_origin_access_identity" "origin_access_identity" {
comment = "OAI for ${local.bucket_name}"
}
# cloudwatch logging and WAF are not required for this example
# tfsec:ignore:avd-aws-0010 tfsec:ignore:avd-aws-0011
resource "aws_cloudfront_distribution" "this" {
origin {
domain_name = aws_s3_bucket.website_bucket.bucket_regional_domain_name
origin_id = aws_s3_bucket.website_bucket.arn
s3_origin_config {
origin_access_identity = aws_cloudfront_origin_access_identity.origin_access_identity.cloudfront_access_identity_path
}
}
enabled = true
is_ipv6_enabled = true
comment = "Cloudfront distribution for ${local.bucket_name}"
default_root_object = "index.html"
default_cache_behavior {
allowed_methods = ["GET", "HEAD"]
cached_methods = ["GET", "HEAD"]
target_origin_id = aws_s3_bucket.website_bucket.arn
viewer_protocol_policy = "redirect-to-https"
forwarded_values {
query_string = false
cookies {
forward = "none"
}
}
}
price_class = "PriceClass_200"
viewer_certificate {
cloudfront_default_certificate = true
}
restrictions {
geo_restriction {
restriction_type = "none"
}
}
}
Now we have all our resources set, it's time to set the intput variables set on the file terraform.tfvars:
aws_access_key = ""
aws_secret_key = ""
aws_region = ""
stack = ""
Now let's run a plan to verify what resources will be created by our code
terraform plan -out plan.tfplan
You should see an output similar to this one:
Now, if everything went well we can apply the plan we run before (be patient because the CDN creation might take a couple of minutes):
terraform apply plan.tfplan
And the output should looks like this one:
That's all! You have your static site running on AWS with S3 and Cloudfront!
If for some strange reason you want to destroy all this infrastructure, you could do it with
terraform destroy
but we promise you don't want to delete this site! Let's see what costs says...
This is the most interesting part.
How much a static website on AWS S3 costs?
The monthly infrastructure costs were calculated having the following data as example:
With the data provided above, we can obtain an estimation similar to this one: AWS Cost Estimation
An approximate cost of $2.88 per year!!
This does not include the services:
Hosting a static website on AWS S3 is an affordable and efficient way to share your ideas with the world. By combining S3 with CloudFront, you can create a high availability setup that delivers your content quickly and reliably to users around the globe.
With Terraform, you can automate the deployment of your infrastructure, making it easy to manage and scale as your project grows. And with Trivy, you can ensure that your code is secure and free from vulnerabilities before it goes live.
So what are you waiting for? Get started today and launch your static website on AWS S3. Your audience is waiting!