hosting a static website in an s3 bucket
In this blog post, we will guide you through the process of hosting a static website in an AWS S3 bucket, coupled with Cloudflare for enhanced performance and security. The provided Terraform scripts establish the necessary AWS and Cloudflare providers, set up inputs for key parameters, create an S3 bucket, configure S3 website settings, enable public access, and prepare for DNS configuration. Follow along to seamlessly deploy and host your static website with this comprehensive guide.
Prerequisites
- aws account
- aws access key
- cloudflare account
- cloudflare access key
- domain added to cloudflare
- basic terraform skills
setting up aws and cloudflare providers
This section focuses on setting up AWS and Cloudflare providers. In the providers.tf file, AWS and Cloudflare providers are configured with specific details such as region, access keys, email and API token, respectively. The versions.tf file specifies the required versions of the Cloudflare and AWS providers using Terraform, ensuring compatibility for the subsequent configuration steps.
in providers.tf
1
2
3
4
5
6
7
8
9
10
provider "aws" {
region = "eu-west-2"
access_key = var.aws_access_key
secret_key = var.aws_secret_key
}
provider "cloudflare" {
email = var.cloudflare_email
api_key = var.cloudflare_token
}
in versions.tf
1
2
3
4
5
6
7
8
9
10
11
12
terraform {
required_providers {
cloudflare = {
source = "cloudflare/cloudflare"
version = "3.33.0"
}
aws = {
source = "hashicorp/aws"
version = "5.31.0"
}
}
}
setting up inputs
This section outlines the setup of input variables in the io.tf file. Key parameters such as AWS access and secret keys, Cloudflare email and token, S3 bucket name, and domain are defined as variables. The default values for the S3 bucket name and domain serve as placeholders, allowing users to customize them based on their specific requirements. These inputs will be utilized in subsequent Terraform configurations for seamless deployment.
in io.tf
1
2
3
4
5
6
7
8
9
10
11
12
13
variable "aws_access_key" {}
variable "aws_secret_key" {}
variable "bucket_name" {
default = "<your domain name>"
}
variable "cloudflare_email" {}
variable "cloudflare_token" {}
variable "domain" {
default = "<your domain name>"
}
create s3 bucket
In this section, an S3 bucket is defined and created in the main.tf file this will be used to store the actual sourcecode of your website. The Terraform script utilizes the aws_s3_bucket resource to create an S3 bucket named “site,” with the bucket name specified by the variable var.bucket_name. This foundational step establishes the core storage component for hosting the static website content on AWS.
in main.tf
1
2
3
resource "aws_s3_bucket" "site" {
bucket = var.bucket_name
}
setup s3 website configuration
This section configures the S3 bucket for website hosting in the main.tf file. Utilizing the aws_s3_bucket_website_configuration resource, it specifies the bucket ID and defines the default index_document as “index.html” which will be used as the homepage for the site and the error_document as “404.html.” which will be returned if someone tries to access part of the site that doesnt exist. This step ensures proper setup for serving web content directly from the S3 bucket, enhancing the overall functionality of the hosted static website.
in main.tf
1
2
3
4
5
6
7
8
9
10
11
resource "aws_s3_bucket_website_configuration" "config" {
bucket = aws_s3_bucket.site.id
index_document {
suffix = "index.html"
}
error_document {
key = "404.html"
}
}
allow public access
This section configures public access settings for the S3 bucket in the main.tf file. The Terraform script includes several resources to manage public access control:
aws_s3_bucket_public_access_block: Ensures that public access block settings are appropriately configured, allowing public access for the static website.
aws_s3_bucket_ownership_controls: Defines ownership controls, specifying “BucketOwnerPreferred” for object ownership.
aws_s3_bucket_acl: Sets the access control list (ACL) for the bucket to “public-read,” allowing public read access to the objects.
aws_s3_bucket_policy: Defines a bucket policy allowing public read access to objects in the S3 bucket.
These configurations collectively establish the necessary permissions for public access to the hosted static website content.
in main.tf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
resource "aws_s3_bucket_public_access_block" "block" {
bucket = aws_s3_bucket.site.id
block_public_acls = false
block_public_policy = false
ignore_public_acls = false
restrict_public_buckets = false
}
resource "aws_s3_bucket_ownership_controls" "site" {
bucket = aws_s3_bucket.site.id
rule {
object_ownership = "BucketOwnerPreferred"
}
}
resource "aws_s3_bucket_acl" "site" {
bucket = aws_s3_bucket.site.id
acl = "public-read"
depends_on = [
aws_s3_bucket_ownership_controls.site,
aws_s3_bucket_public_access_block.block
]
}
resource "aws_s3_bucket_policy" "site" {
bucket = aws_s3_bucket.site.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Sid = "PublicReadGetObject"
Effect = "Allow"
Principal = "*"
Action = "s3:GetObject"
Resource = [
aws_s3_bucket.site.arn,
"${aws_s3_bucket.site.arn}/*",
]
},
]
})
depends_on = [
aws_s3_bucket_public_access_block.block
]
}
upload html to s3 bucket (coming soon)
in main.tf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
resource "aws_iam_user" "github_actions" {
name = "${var.bucket_name}-write-github-actions"
}
resource "aws_iam_access_key" "github_actions" {
user = aws_iam_user.github_actions.name
}
resource "aws_iam_policy" "github_actions" {
name = "${var.bucket_name}-write-github-actions"
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Sid = "AllowWriteToBucket"
Effect = "Allow"
Action = [
"s3:PutObject",
"s3:PutObjectAcl",
"s3:DeleteObject",
"s3:DeleteObjectVersion",
"s3:ListBucket",
"s3:ListBucketVersions",
"s3:ListBucketMultipartUploads",
"s3:AbortMultipartUpload",
]
Resource = [
aws_s3_bucket.site.arn,
"${aws_s3_bucket.site.arn}/*",
]
},
]
})
}
resource "aws_iam_user_policy_attachment" "github_actions" {
user = aws_iam_user.github_actions.name
policy_arn = aws_iam_policy.github_actions.arn
}
in io.tf
1
2
3
4
5
6
7
8
output "aws_access_key_site_write" {
value = aws_iam_access_key.github_actions.id
}
output "aws_secret_key_site_write" {
value = aws_iam_access_key.github_actions.secret
sensitive = true
}
configure dns
This section focuses on configuring DNS settings for the hosted static website using Cloudflare. The initial Terraform configuration establishes the required Cloudflare provider version. Subsequently, the script fetches information about the Cloudflare zone associated with the specified domain. Two Cloudflare records are then created: one for the main domain (site_cname) and another for the “www” subdomain. These records are set as CNAME (Canonical Name) entries, linking them to the AWS S3 bucket’s website endpoint. The configurations ensure seamless integration between Cloudflare and the static website hosted on AWS S3.
in main.tf
1
2
3
4
5
6
7
8
terraform {
required_providers {
cloudflare = {
source = "cloudflare/cloudflare"
version = "3.33.0"
}
}
}
in main.tf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
data "cloudflare_zone" "domain" {
name = var.domain
}
resource "cloudflare_record" "site_cname" {
zone_id = data.cloudflare_zone.domain.id
name = var.domain
value = aws_s3_bucket_website_configuration.config.website_endpoint
type = "CNAME"
ttl = 1
proxied = true
allow_overwrite = true
comment = "This is the CNAME for the site"
tags = []
}
resource "cloudflare_record" "www" {
zone_id = data.cloudflare_zone.domain.id
name = "www"
value = var.domain
type = "CNAME"
ttl = 1
proxied = true
allow_overwrite = true
comment = "This is the CNAME for the site"
tags = []
}