Compare commits

...

6 Commits

Author SHA1 Message Date
64c297a905 Merge pull request 'modularise?' (#1) from module into master
All checks were successful
Terraform validate and apply / terraform (ubuntu-latest) (push) Successful in 7m30s
Reviewed-on: #1
2025-09-18 15:48:39 +00:00
39c02779a9 fix: set env var for multiple domains
All checks were successful
Terraform validate and apply / terraform (ubuntu-latest) (pull_request) Successful in 1m45s
2025-09-18 10:34:45 -05:00
daf180c210 fmt: formatting update
Some checks failed
Terraform validate and apply / terraform (ubuntu-latest) (pull_request) Failing after 1m42s
2025-09-18 10:28:01 -05:00
ba2e1e4655 also run plan for PRs
Some checks failed
Terraform validate and apply / terraform (ubuntu-latest) (pull_request) Failing after 5m27s
2025-09-18 10:03:48 -05:00
615fc2a24f modules? 2025-09-18 09:59:37 -05:00
6e106657ea modularise? 2025-09-18 01:10:57 -05:00
9 changed files with 276 additions and 212 deletions

View File

@@ -4,19 +4,23 @@ on:
push:
branches:
- master
pull_request:
branches:
- master
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
TF_VAR_aws_region: ${{ vars.TF_VAR_aws_region }}
TF_VAR_site_domain: ${{ vars.TF_VAR_site_domain }}
TF_VAR_site_domains: ${{ vars.TF_VAR_site_domains }}
TF_VAR_project_name: ${{ vars.TF_VAR_project_name }}
TF_VAR_environment: ${{ vars.TF_VAR_environment }}
TF_VAR_tuffas_applier_role_arn: ${{ vars.TF_VAR_tuffas_applier_role_arn }}
TF_VAR_tfstate_backend_role_arn: ${{ vars.TF_VAR_tfstate_backend_role_arn }}
TF_PLUGIN_CACHE_DIR: ${{ github.workspace }}/.terraform.d/plugin-cache
jobs:
nix:
terraform:
strategy:
fail-fast: true
matrix:

206
main.tf
View File

@@ -8,202 +8,12 @@ provider "aws" {
provider "cloudflare" {
}
locals {
common_tags = {
Project = var.project_name
Environment = var.environment
ManagedBy = "terraform"
Domain = var.site_domain
}
}
# block with type resource, uses provider's aws_s3_bucket resource type and names it site
resource "aws_s3_bucket" "site" {
# presumably
bucket = var.site_domain
tags = local.common_tags
}
# necessary to allow public users to eventually hit the s3 bucket
resource "aws_s3_bucket_public_access_block" "site" {
bucket = aws_s3_bucket.site.id
block_public_acls = false
block_public_policy = false
ignore_public_acls = false
restrict_public_buckets = false
}
# name says it all
resource "aws_s3_bucket_website_configuration" "site" {
bucket = aws_s3_bucket.site.id
# Note that this isn't an =, i don't know why
index_document {
suffix = "index.html"
}
error_document {
key = "error.html"
}
}
# This controls the ownership of the objects inside the bucket upon upload
# If possible, this sets the ownership of objects to the bucket owner
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.site
]
}
# Public read access for website objects only (not bucket itself)
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}/*"
},
]
})
depends_on = [
aws_s3_bucket_public_access_block.site
]
}
# Cloudflare time
# data block is about retrieving data from ext. source, not configuring a resource that lives in state
data "cloudflare_zones" "domain" {
# why a filter?
filter {
name = var.site_domain
}
}
# DNS setup
resource "cloudflare_record" "site_cname" {
zone_id = data.cloudflare_zones.domain.zones[0].id
name = var.site_domain
value = aws_s3_bucket_website_configuration.site.website_endpoint
type = "CNAME"
ttl = 1
proxied = true
}
resource "cloudflare_record" "www" {
zone_id = data.cloudflare_zones.domain.zones[0].id
name = "www"
value = var.site_domain
type = "CNAME"
ttl = 1
proxied = true
}
# S3 bucket versioning for content protection
resource "aws_s3_bucket_versioning" "site" {
bucket = aws_s3_bucket.site.id
versioning_configuration {
status = "Enabled"
}
}
# Access logging bucket for monitoring
# resource "aws_s3_bucket" "access_logs" {
# bucket = "${var.site_domain}-access-logs"
#
# tags = merge(local.common_tags, {
# Purpose = "access-logs"
# })
# }
# resource "aws_s3_bucket_public_access_block" "access_logs" {
# bucket = aws_s3_bucket.access_logs.id
#
# block_public_acls = true
# block_public_policy = true
# ignore_public_acls = true
# restrict_public_buckets = true
# }
# Enable access logging for the main site bucket
# resource "aws_s3_bucket_logging" "site" {
# bucket = aws_s3_bucket.site.id
#
# target_bucket = aws_s3_bucket.access_logs.id
# target_prefix = "access-logs/"
# }
# Lifecycle rules for cost management
resource "aws_s3_bucket_lifecycle_configuration" "site" {
bucket = aws_s3_bucket.site.id
rule {
id = "cleanup_old_versions"
status = "Enabled"
noncurrent_version_expiration {
noncurrent_days = 90
}
filter {
prefix = ""
}
}
rule {
id = "cleanup_incomplete_uploads"
status = "Enabled"
abort_incomplete_multipart_upload {
days_after_initiation = 7
}
filter {
prefix = ""
}
}
}
# Lifecycle rules for access logs
# resource "aws_s3_bucket_lifecycle_configuration" "access_logs" {
# bucket = aws_s3_bucket.access_logs.id
#
# rule {
# id = "delete_old_logs"
# status = "Enabled"
#
# expiration {
# days = 90
# }
# }
# }
resource "cloudflare_page_rule" "https" {
zone_id = data.cloudflare_zones.domain.zones[0].id
target = "*.${var.site_domain}/*"
actions {
always_use_https = true
}
module "static_website" {
source = "./modules/static-website"
# toset to dedupe
for_each = toset(var.site_domains)
site_domain = each.key
project_name = var.project_name
environment = var.environment
}

View File

@@ -0,0 +1,141 @@
locals {
common_tags = {
Project = var.project_name
Environment = var.environment
ManagedBy = "terraform"
Domain = var.site_domain
}
}
resource "aws_s3_bucket" "site" {
bucket = var.site_domain
tags = local.common_tags
}
resource "aws_s3_bucket_public_access_block" "site" {
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_website_configuration" "site" {
bucket = aws_s3_bucket.site.id
index_document {
suffix = "index.html"
}
error_document {
key = "error.html"
}
}
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.site
]
}
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}/*"
},
]
})
depends_on = [
aws_s3_bucket_public_access_block.site
]
}
data "cloudflare_zones" "domain" {
filter {
name = var.site_domain
}
}
resource "cloudflare_record" "site_cname" {
zone_id = data.cloudflare_zones.domain.zones[0].id
name = var.site_domain
value = aws_s3_bucket_website_configuration.site.website_endpoint
type = "CNAME"
ttl = 1
proxied = true
}
resource "cloudflare_record" "www" {
zone_id = data.cloudflare_zones.domain.zones[0].id
name = "www"
value = var.site_domain
type = "CNAME"
ttl = 1
proxied = true
}
resource "aws_s3_bucket_versioning" "site" {
bucket = aws_s3_bucket.site.id
versioning_configuration {
status = "Enabled"
}
}
resource "aws_s3_bucket_lifecycle_configuration" "site" {
bucket = aws_s3_bucket.site.id
rule {
id = "cleanup_old_versions"
status = "Enabled"
noncurrent_version_expiration {
noncurrent_days = 90
}
filter {
prefix = ""
}
}
rule {
id = "cleanup_incomplete_uploads"
status = "Enabled"
abort_incomplete_multipart_upload {
days_after_initiation = 7
}
filter {
prefix = ""
}
}
}
resource "cloudflare_page_rule" "https" {
zone_id = data.cloudflare_zones.domain.zones[0].id
target = "*.${var.site_domain}/*"
actions {
always_use_https = true
}
}

View File

@@ -0,0 +1,14 @@
output "website_bucket_name" {
description = "Name (id) of the bucket"
value = aws_s3_bucket.site.id
}
output "bucket_endpoint" {
description = "Bucket endpoint"
value = aws_s3_bucket_website_configuration.site.website_endpoint
}
output "domain_name" {
description = "Website endpoint"
value = var.site_domain
}

View File

@@ -0,0 +1,20 @@
variable "site_domain" {
type = string
description = "The domain name of the site"
}
variable "project_name" {
type = string
description = "Name of the project for resource tagging"
default = "tuffas"
}
variable "environment" {
type = string
description = "Environment name (e.g., dev, staging, prod)"
validation {
condition = contains(["dev", "staging", "prod"], var.environment)
error_message = "Environment must be one of: dev, staging, prod."
}
}

View File

@@ -0,0 +1,14 @@
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.92"
}
cloudflare = {
source = "cloudflare/cloudflare"
version = "~> 2.19.2"
}
}
required_version = ">= 1.2"
}

60
moved.tf Normal file
View File

@@ -0,0 +1,60 @@
moved {
from = aws_s3_bucket.site
to = module.static_website.aws_s3_bucket.site
}
moved {
from = aws_s3_bucket_acl.site
to = module.static_website.aws_s3_bucket_acl.site
}
moved {
from = aws_s3_bucket_lifecycle_configuration.site
to = module.static_website.aws_s3_bucket_lifecycle_configuration.site
}
moved {
from = aws_s3_bucket_ownership_controls.site
to = module.static_website.aws_s3_bucket_ownership_controls.site
}
moved {
from = aws_s3_bucket_policy.site
to = module.static_website.aws_s3_bucket_policy.site
}
moved {
from = aws_s3_bucket_public_access_block.site
to = module.static_website.aws_s3_bucket_public_access_block.site
}
moved {
from = aws_s3_bucket_versioning.site
to = module.static_website.aws_s3_bucket_versioning.site
}
moved {
from = aws_s3_bucket_website_configuration.site
to = module.static_website.aws_s3_bucket_website_configuration.site
}
moved {
from = cloudflare_record.www
to = module.static_website.cloudflare_record.www
}
moved {
from = cloudflare_record.site_cname
to = module.static_website.cloudflare_record.site_cname
}
moved {
from = cloudflare_page_rule.https
to = module.static_website.cloudflare_page_rule.https
}
moved {
from = module.static_website
to = module.static_website["hruday.me"]
}

View File

@@ -1,14 +1,14 @@
output "website_bucket_name" {
description = "Name (id) of the bucket"
value = aws_s3_bucket.site.id
output "website_bucket_names" {
description = "Names (ids) of the buckets by domain"
value = { for k, v in module.static_website : k => v.website_bucket_name }
}
output "bucket_endpoint" {
description = "Bucket endpoint"
value = aws_s3_bucket_website_configuration.site.website_endpoint
output "bucket_endpoints" {
description = "Bucket endpoints by domain"
value = { for k, v in module.static_website : k => v.bucket_endpoint }
}
output "domain_name" {
description = "Website endpoint"
value = var.site_domain
output "domain_names" {
description = "Website endpoints by domain"
value = { for k, v in module.static_website : k => v.domain_name }
}

View File

@@ -1,11 +1,11 @@
variable "aws_region" {
type = string
description = "The AWS region of this site"
description = "The AWS region of these sites"
}
variable "site_domain" {
type = string
description = "The domain name of the site"
variable "site_domains" {
type = list(any)
description = "The domain name of these sites, which will be mapped over"
}
variable "tuffas_applier_role_arn" {
@@ -19,6 +19,7 @@ variable "project_name" {
default = "tuffas"
}
# future proofing
variable "environment" {
type = string
description = "Environment name (e.g., dev, staging, prod)"