top of page
  • Writer's pictureEmily T. Burak

AWS Static S3 Website Terraform Module



Recently, I wanted to practice writing Terraform modules, which I had not done in a few months. With some looking around, I couldn't find an MVP for setting up a static website serving HTML assets on my preferred cloud, AWS, using S3 hosting. So I decided to take a shot at it myself.


Challenges:

To begin, I found a good amount of the resources on the web for this were outdated. Terraform's ecosystem and providers move fast, so this wasn't a surprise. What I could find that was still relevant was useful for updating to more current code, such as reconciling and revising code with resource types that had been split off from each other into new resources.


Approach:

I used the AWS provider ( hashicorp/aws ) solely for this project, attempting to use up-to-date resources based on the documentation. The AWS community modules have been great in my experience using them for EKS Managed Node Groups, but I wanted to stay low-level and simple here.


First, I worked to, in this modularized framework (which means you'll have to add your own remote state backend, as this is agnostic to backends), set up the AWS provider:


From my main.tf

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~>3.27"
    }
  }
  
 provider "aws" {
  region = var.region
}

A brief digression about var.region, here are the variables I set up in my variables.tf for this static site hosting generation:



variable "bucket_name" {
  description = "Name of your app/bucket"
  type        = string
}

variable "region" {
  description = "Region in which to deploy AWS resources"
  default     = "us-west-2"
  type        = string
}

variable "index_page" {
  description = "Index page path for your static website"
  default     = "index.html"
  type        = string
}

variable "error_page" {
  description = "Error page path for your static website."
  default     = "error.html"
  type        = string
} 

Then, I worked on defining the bucket itself, allowing appropriate access through IAM policies, and setting up the static web hosting:



resource "aws_s3_bucket" "static_site_bucket" {
  bucket = var.bucket_name

}
resource "aws_s3_bucket_ownership_controls" "static_site_bucket_ownership_controls" {
  bucket = aws_s3_bucket.static_site_bucket.id
  rule {
    object_ownership = "BucketOwnerPreferred"
  }
}

resource "aws_s3_bucket_public_access_block" "static_site_bucket_public_access" {
  bucket = aws_s3_bucket.static_site_bucket.id

  block_public_acls       = false
  block_public_policy     = false
  ignore_public_acls      = false
  restrict_public_buckets = false
}

resource "aws_s3_bucket_policy" "static_site_bucket_policy" {
  bucket = var.bucket_name

  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Sid       = "PublicReadGetObject"
        Principal = "*"
        Action = [
          "s3:GetObject",
        ]
        Effect = "Allow"
        Resource = [
          "arn:aws:s3:::${var.bucket_name}",
          "arn:aws:s3:::${var.bucket_name}/*"
        ]
      },
    ]
  })

  depends_on = [aws_s3_bucket_public_access_block.static_site_bucket_public_access]
}

resource "aws_s3_bucket_website_configuration" "static_site_bucket_website_config" {
  bucket = var.bucket_name
  index_document {
    suffix = var.index_page
  }

  error_document {
    key = var.error_page
  }
}

To rundown these resources:

static_site_bucket: This is the bucket from which the content is served.

static_site_bucket_public_access: A variety of settings to allow for public access.

static_site_bucket_policy: The IAM bucket policy gives the ability for anyone to read the bucket and its contents.

static_site_bucket_website_config: Lastly, this sets up static website hosting on S3.


Finally, I made sure to output the s3 endpoint for ease of use at outputs.tf


output "static_site_endpoint" {
  value = aws_s3_bucket_website_configuration.static_site_bucket_website_config.website_endpoint
}

There are some quirks and hurdles at this point: I attempted to be able to include a src folder with an index.html and error.html in it and set those pages up automagically but it honestly did not play well with the aws_s3_bucket_website_configuration resource type as I'd set it up, even erasing the static website hosting setting. Definitely worth investigating more and getting that set up in the future.

That's to say, you'll have to add your own index.html and error.html to the bucket after provisioning to get content on the static site when using this module.


The full code can be found at: https://github.com/EmilyBurak/tf-static-site-s3


You can follow me on LinkedIn at: https://www.linkedin.com/in/emily-burak/


CREDITS/REFERENCES: - https://github.com/GeminiWind/terraform-aws-static-website - https://dev.to/aws-builders/build-a-static-website-using-s3-route-53-with-terraform-1ele - https://stackoverflow.com/questions/76419099/access-denied-when-creating-s3-bucket-acl-s3-policy-using-terraform - Terraform docs, of course!


345 views0 comments
bottom of page