Merge pull request 'modularise?' (#1) from module into master
All checks were successful
Terraform validate and apply / terraform (ubuntu-latest) (push) Successful in 7m30s
All checks were successful
Terraform validate and apply / terraform (ubuntu-latest) (push) Successful in 7m30s
Reviewed-on: #1
This commit was merged in pull request #1.
This commit is contained in:
@@ -4,19 +4,23 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- master
|
- master
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
env:
|
env:
|
||||||
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||||
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||||
TF_VAR_aws_region: ${{ vars.TF_VAR_aws_region }}
|
TF_VAR_aws_region: ${{ vars.TF_VAR_aws_region }}
|
||||||
TF_VAR_site_domain: ${{ vars.TF_VAR_site_domain }}
|
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_project_name: ${{ vars.TF_VAR_project_name }}
|
||||||
TF_VAR_environment: ${{ vars.TF_VAR_environment }}
|
TF_VAR_environment: ${{ vars.TF_VAR_environment }}
|
||||||
TF_VAR_tuffas_applier_role_arn: ${{ vars.TF_VAR_tuffas_applier_role_arn }}
|
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_VAR_tfstate_backend_role_arn: ${{ vars.TF_VAR_tfstate_backend_role_arn }}
|
||||||
TF_PLUGIN_CACHE_DIR: ${{ github.workspace }}/.terraform.d/plugin-cache
|
TF_PLUGIN_CACHE_DIR: ${{ github.workspace }}/.terraform.d/plugin-cache
|
||||||
jobs:
|
jobs:
|
||||||
nix:
|
terraform:
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: true
|
fail-fast: true
|
||||||
matrix:
|
matrix:
|
||||||
|
|||||||
206
main.tf
206
main.tf
@@ -8,202 +8,12 @@ provider "aws" {
|
|||||||
provider "cloudflare" {
|
provider "cloudflare" {
|
||||||
}
|
}
|
||||||
|
|
||||||
locals {
|
module "static_website" {
|
||||||
common_tags = {
|
source = "./modules/static-website"
|
||||||
Project = var.project_name
|
|
||||||
Environment = var.environment
|
# toset to dedupe
|
||||||
ManagedBy = "terraform"
|
for_each = toset(var.site_domains)
|
||||||
Domain = var.site_domain
|
site_domain = each.key
|
||||||
}
|
project_name = var.project_name
|
||||||
}
|
environment = var.environment
|
||||||
|
|
||||||
# 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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
141
modules/static-website/main.tf
Normal file
141
modules/static-website/main.tf
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
14
modules/static-website/outputs.tf
Normal file
14
modules/static-website/outputs.tf
Normal 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
|
||||||
|
}
|
||||||
20
modules/static-website/variables.tf
Normal file
20
modules/static-website/variables.tf
Normal 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."
|
||||||
|
}
|
||||||
|
}
|
||||||
14
modules/static-website/versions.tf
Normal file
14
modules/static-website/versions.tf
Normal 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
60
moved.tf
Normal 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"]
|
||||||
|
}
|
||||||
18
outputs.tf
18
outputs.tf
@@ -1,14 +1,14 @@
|
|||||||
output "website_bucket_name" {
|
output "website_bucket_names" {
|
||||||
description = "Name (id) of the bucket"
|
description = "Names (ids) of the buckets by domain"
|
||||||
value = aws_s3_bucket.site.id
|
value = { for k, v in module.static_website : k => v.website_bucket_name }
|
||||||
}
|
}
|
||||||
|
|
||||||
output "bucket_endpoint" {
|
output "bucket_endpoints" {
|
||||||
description = "Bucket endpoint"
|
description = "Bucket endpoints by domain"
|
||||||
value = aws_s3_bucket_website_configuration.site.website_endpoint
|
value = { for k, v in module.static_website : k => v.bucket_endpoint }
|
||||||
}
|
}
|
||||||
|
|
||||||
output "domain_name" {
|
output "domain_names" {
|
||||||
description = "Website endpoint"
|
description = "Website endpoints by domain"
|
||||||
value = var.site_domain
|
value = { for k, v in module.static_website : k => v.domain_name }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
variable "aws_region" {
|
variable "aws_region" {
|
||||||
type = string
|
type = string
|
||||||
description = "The AWS region of this site"
|
description = "The AWS region of these sites"
|
||||||
}
|
}
|
||||||
|
|
||||||
variable "site_domain" {
|
variable "site_domains" {
|
||||||
type = string
|
type = list(any)
|
||||||
description = "The domain name of the site"
|
description = "The domain name of these sites, which will be mapped over"
|
||||||
}
|
}
|
||||||
|
|
||||||
variable "tuffas_applier_role_arn" {
|
variable "tuffas_applier_role_arn" {
|
||||||
@@ -19,6 +19,7 @@ variable "project_name" {
|
|||||||
default = "tuffas"
|
default = "tuffas"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# future proofing
|
||||||
variable "environment" {
|
variable "environment" {
|
||||||
type = string
|
type = string
|
||||||
description = "Environment name (e.g., dev, staging, prod)"
|
description = "Environment name (e.g., dev, staging, prod)"
|
||||||
|
|||||||
Reference in New Issue
Block a user