Skip to content

Terraform AWS Lightweight Hosting

A Terraform module that provides an opinated solution for hosting lightweight websites using AWS S3 and AWS Lambda.

License:
MIT
Version:
6.1.0

This Terraform module was developed to simplify hosting sites created using Static Site Generation (SSG) frameworks. I use it to host the web properties I personally manage.

In order to use this module, you must:

  • Have a completely static frontend.
    I highly recommend using a static site generator like Astro, Gatsby or Next.js.

  • Manage your domain name via Route 53.
    Route 53 is used to automatically provision ACM certificates. While I could organize this module to remove this requirement, as stated above, this module is opinated 🙂.

If no lambda parameters are specified, no lambda will be deployed.

AWS Cloud Visitors Amazon S3 Bucket (Static Website) Amazon CloudFront Distribution Amazon API Gateway Endpoint AWS Lambda (Backend) Amazon Route 53 Hosted Zone ACM Certificate
OpenTofu Support

This Terraform module supports both Terraform and OpenTofu.

Warning

This Terraform stack will be unable to serve files without any extensions.

Since CloudFront doesn’t natively support directory index documents, this project includes a CloudFront Function to emulate the functionality. Unfortunately, this breaks support for files without any extensions.

Warning

CloudFront doesn’t support custom error pages per origin, and instead applies custom responses globally.

In order to provide support for custom 404 documents, a custom error response is defined for HTTP 403 and HTTP 404 responses. Therefore, any API response with a 403 or 404 status code will return a 404 response with your error_document as the body of the response.

Examples

Basic Usage with no Lambda

module "static_hosting" {
  # Alternatively, you may use
  # source = "git::https://gitlab.com/finewolf-projects/terraform-aws-lightweight-hosting.git?ref=v6.1.0"

  source = "gitlab.com/finewolf-projects/terraform-aws-lightweight-hosting/aws"
  version = "6.1.0"

  domains  = ["example.org", "www.example.org"]
  zone_ids = ["Z00000000000000000000", "Z00000000000000000000"]

  index_document = "index.html"
  error_document = "404.html"
}

Usage with a Lambda Backend

module "static_hosting" {
  source = "gitlab.com/finewolf-projects/terraform-aws-lightweight-hosting/aws"
  version = "6.1.0"

  domains  = ["example.org", "www.example.org"]
  zone_ids = ["Z00000000000000000000", "Z00000000000000000000"]

  index_document = "index.html"
  error_document = "404.html"

  lambda_memory_size = 256
  lambda_package_config = {
    filename  = "/home/ci/builds/node-application.zip"
    s3_bucket = aws_s3_bucket.storage_bucket.id
    s3_key    = "lambda/node-application.zip"
    runtime   = "nodejs18.x"
    handler   = "index.handler"
  }

  lambda_timeout       = 20
  lambda_log_retention = 7

  lambda_environment = {
    ThisIs__ACustom__EnvVar = "HelloWorld"
  }
}

# Storage bucket for artifacts
resource "aws_s3_bucket" "storage_bucket" {
  bucket = "example.org-artifacts"
}

resource "aws_s3_bucket_ownership_controls" "storage_bucket_ownership" {
  bucket = aws_s3_bucket.storage_bucket.id

  rule {
    object_ownership = "BucketOwnerEnforced"
  }
}

resource "aws_s3_bucket_public_access_block" "bucket_public_access_block" {
  bucket = aws_s3_bucket.storage_bucket.id

  block_public_acls       = true
  block_public_policy     = true
  ignore_public_acls      = true
  restrict_public_buckets = true
}

resource "aws_s3_bucket_versioning" "bucket_versioning" {
  bucket = aws_s3_bucket.storage_bucket.id

  versioning_configuration {
    status = "Suspended"
  }
}