Hugo Terraform S3 Cdn

Introduction

Amazon Web Services' S3, CDN, Route53 and ACM are a great way to create and host a static website for basically free. I’ve been running this site and a variety of static blogs (gregsharpe.co.uk) using these services for a few years now. Not once have the monthly costs gone over $0.51, and the $0.50 is a for the Route53 public hosted zone. (Disclaimer, my little blog sites get maybe 10/20 views per day)

Terraform

Combine the easy to use services above with Terraform, and everything becomes a little easier. Here’s a working example of everything you need.

Route53

resource aws_route53_zone sharpe_wales {
  name = "sharpe.wales"

  tags = {
    "Environment" = "Production"
    "Email"       = "awsproduction+me@gregsharpe.co.uk"
    "Account"     = "gregsharpe-production"
  }
}

Cloudposse Terraform module

module "greg_sharpe_wales" {
  source                   = "cloudposse/cloudfront-s3-cdn/aws"
  version                  = "0.23.1"
  namespace                = "greg.sharpe.wales"
  stage                    = "prod"
  name                     = "s3"
  aliases                  = ["greg.sharpe.wales"]
  parent_zone_id           = aws_route53_zone.sharpe_wales.id
  index_document           = "index.html"
  logging_enabled          = "false"
  acm_certificate_arn      = aws_acm_certificate.greg_sharpe_wales.arn
  minimum_protocol_version = "TLSv1.2_2018"
  price_class              = "PriceClass_100"
  cached_methods           = ["GET", "HEAD", "OPTIONS"]
  cors_allowed_headers     = ["*"]
  cors_allowed_origins     = ["*"]
  website_enabled = true
  routing_rules = <<EOF
  [{
    "Condition": {
      "KeyPrefixEquals": "/"
    },
    "Redirect": {
      "ReplaceKeyWith": "index.html"
    }
  }]
  EOF

  tags = {
    "Environment" = "Production"
    "Email"       = "awsprod+me@gregsharpe.co.uk"
    "Account"     = "gregsharpe-prod"
  }
}

Amazon Managed Certificate

The AWS certificate has to be created within the us-east-1 region, when working with CloudFront.

provider aws {
  region = "us-east-1"
  alias  = "us-east-1"
}

resource aws_acm_certificate greg_sharpe_wales {
  provider = aws.us-east-1

  domain_name       = "greg.sharpe.wales"
  validation_method = "DNS"

  lifecycle {
    create_before_destroy = true
  }

  tags = {
    "Environment" = "Production"
    "Email"       = "awsprod+me@gregsharpe.co.uk"
    "Account"     = "gregsharpe-prod"
  }
}

resource "aws_route53_record" "greg_sharpe_wales_cert_validation" {
  provider = aws.us-east-1

  zone_id = aws_route53_zone.sharpe_wales.id
  name    = aws_acm_certificate.greg_sharpe_wales.domain_validation_options[0].resource_record_name
  type    = aws_acm_certificate.greg_sharpe_wales.domain_validation_options[0].resource_record_type
  ttl     = 60

  records = [
    aws_acm_certificate.greg_sharpe_wales.domain_validation_options[0].resource_record_value
  ]

}

The End

Check out a working example here.