Hosting a Static Website

S3 can host a static website. You just need to change a few configurations on the bucket to turn an S3 bucket into a host for your website.

CodeBuild is a fully managed continuous integration service that compiles source code, runs tests, and produces software packages that are ready to deploy. In order to tell CodeBuild what to do and how to do things, we need to provide a special YAML file named buildspec.yaml. One of the great features of CodeBuild is its ability to run any code in the cloud environment. We will be using this feature to upload our website's content to S3. CodeBuild can also connect to a GitHub repo and listen to changes applied there. It can then run any code once a change is detected (like when you push a new update for your website).

With just S3 and CodeBuild, we can create a CI/CD pipeline to host a static website. We'll be demonstrating this first manually in the class. Then we'll be using Terraform to automate the process, so we can easily spin up a pipeline for any static website in a minute in the future. What follows next is the Terraform code needed for creating the pipeline.

main.tf

# create S3 bucket to host the website resource "aws_s3_bucket" "site_bucket" { bucket = var.bucket_name } # make sure public access on the bucket is not blocked resource "aws_s3_bucket_public_access_block" "this" { bucket = aws_s3_bucket.site_bucket.id block_public_acls = false block_public_policy = false ignore_public_acls = false restrict_public_buckets = false } # create bucket policy to allow public read access # without this, no one would be able to view the website resource "aws_s3_bucket_policy" "allow_public_access" { bucket = aws_s3_bucket.site_bucket.id policy = <<POLICY { "Version": "2012-10-17", "Id": "Policy1664159397662", "Statement": [ { "Sid": "Stmt1664159396184", "Effect": "Allow", "Principal": "*", "Action": "s3:GetObject", "Resource": "${aws_s3_bucket.site_bucket.arn}/*" } ] } POLICY } # configure the S3 bucket as a statuc website resource "aws_s3_bucket_website_configuration" "my_website" { bucket = aws_s3_bucket.site_bucket.id index_document { suffix = var.index_document } } # create a CodeBuild project resource "aws_codebuild_project" "project" { name = var.project_name build_timeout = "20" service_role = aws_iam_role.cbuild_role.arn artifacts { type = "NO_ARTIFACTS" } environment { compute_type = "BUILD_GENERAL1_SMALL" image = "aws/codebuild/standard:4.0" type = "LINUX_CONTAINER" # passing the bucket name as an environment variable environment_variable { name = "BUCKET_NAME" value = "s3://${var.bucket_name}" } } logs_config { cloudwatch_logs { group_name = "build-${var.project_name}" } } # set the source to our repo source { type = "GITHUB" location = var.repo_address git_clone_depth = 1 } # set the branch name for the pipeline source_version = var.branch_name } # create a role for CodeBuild service to assume resource "aws_iam_role" "cbuild_role" { name = var.project_name assume_role_policy = <<EOF { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "codebuild.amazonaws.com" }, "Action": "sts:AssumeRole" } ] } EOF } # attach an IAM policy to CodeBuild role # for accessing the S3 bucket resource "aws_iam_role_policy" "this" { role = aws_iam_role.cbuild_role.name policy = <<POLICY { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Resource": [ "*" ], "Action": [ "logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents" ] }, { "Effect": "Allow", "Action": [ "s3:*" ], "Resource": ["${aws_s3_bucket.site_bucket.arn}", "${aws_s3_bucket.site_bucket.arn}/*"] } ] } POLICY } # create a webhook so the projects can detect # changes in the repo and start a new build # note that you need to remove this section (or comment out) # and build whatever's above this resource first # you then need to change the source section of the # CodeBuild project to connect to your GitHub repo # after that, you can create this resource resource "aws_codebuild_webhook" "this" { project_name = aws_codebuild_project.project.name build_type = "BUILD" filter_group { filter { type = "EVENT" pattern = "PUSH" } filter { type = "HEAD_REF" pattern = var.branch_name } } }

variables.tf

variable "bucket_name" { type = string } variable "index_document" { type = string default = "index.html" } variable "project_name" { type = string } variable "repo_address" { type = string } variable "branch_name" { type = string default = "main" }

terraform.tfvars

bucket_name = "my-awesome-website-32989" repo_address = "<REPO-LINK>" project_name = "awesome-website"

outputs.tf

# output the address of the static website output "website_address" { value = aws_s3_bucket_website_configuration.my_website.website_endpoint }

versions.tf

terraform { required_providers { aws = { source = "hashicorp/aws" } } } provider "aws" { region = "ca-central-1" }

buildspec.yml

version: 0.2 phases: build: commands: - aws s3 sync . $BUCKET_NAME --exclude ".git/*" post_build: commands: - echo Success!