diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f876c570..a4e89459 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -37,6 +37,6 @@ repos: - repo: https://github.com/tofuutils/pre-commit-opentofu rev: v1.0.4 hooks: + - id: tofu_validate - id: tofu_fmt - id: tofu_tflint - - id: tofu_validate diff --git a/web/deploy/terraform/modules/shared_resources/main.tf b/web/deploy/terraform/shared/networking/main.tf similarity index 75% rename from web/deploy/terraform/modules/shared_resources/main.tf rename to web/deploy/terraform/shared/networking/main.tf index 905c429b..ba856059 100644 --- a/web/deploy/terraform/modules/shared_resources/main.tf +++ b/web/deploy/terraform/shared/networking/main.tf @@ -9,34 +9,6 @@ terraform { } } -# tflint-ignore: terraform_unused_declarations -variable "aws_region" { - description = "AWS region" - default = "us-east-1" - type = string -} - -# tflint-ignore: terraform_unused_declarations -variable "s3_bucket" { - description = "S3 bucket for Terraform state" - default = "osm-storage" - type = string -} - -# tflint-ignore: terraform_unused_declarations -variable "dynamodb_table" { - description = "DynamoDB table for Terraform state locking" - default = "terraform-locks" - type = string -} - -# tflint-ignore: terraform_unused_declarations -variable "ssh_port" { - description = "Non-standard port for SSH" - default = 22 - type = number -} - # VPC resource "aws_vpc" "main" { cidr_block = "10.0.0.0/16" @@ -192,29 +164,3 @@ data "aws_ami" "ubuntu" { values = ["ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*"] } } - - - - -# Outputs -output "vpc_id" { - value = aws_vpc.main.id -} -output "subnet_id" { - value = aws_subnet.main.id -} -output "security_group_id" { - value = aws_security_group.allow_all.id -} -output "internet_gateway_id" { - value = aws_internet_gateway.main.id -} -output "route_table_id" { - value = aws_route_table.main.id -} -output "aws_network_acl_id" { - value = aws_network_acl.allow_all.id -} -output "ami_id" { - value = data.aws_ami.ubuntu.id -} diff --git a/web/deploy/terraform/shared/networking/outputs.tf b/web/deploy/terraform/shared/networking/outputs.tf new file mode 100644 index 00000000..07d8bd0b --- /dev/null +++ b/web/deploy/terraform/shared/networking/outputs.tf @@ -0,0 +1,27 @@ +output "vpc_id" { + value = aws_vpc.main.id +} + +output "subnet_id" { + value = aws_subnet.main.id +} + +output "security_group_id" { + value = aws_security_group.allow_all.id +} + +output "internet_gateway_id" { + value = aws_internet_gateway.main.id +} + +output "route_table_id" { + value = aws_route_table.main.id +} + +output "aws_network_acl_id" { + value = aws_network_acl.allow_all.id +} + +output "ami_id" { + value = data.aws_ami.ubuntu.id +} diff --git a/web/deploy/terraform/shared/networking/variables.tf b/web/deploy/terraform/shared/networking/variables.tf new file mode 100644 index 00000000..ceae8d73 --- /dev/null +++ b/web/deploy/terraform/shared/networking/variables.tf @@ -0,0 +1,27 @@ +# tflint-ignore: terraform_unused_declarations +variable "aws_region" { + description = "AWS region" + default = "us-east-1" + type = string +} + +# tflint-ignore: terraform_unused_declarations +variable "s3_bucket" { + description = "S3 bucket for Terraform state" + default = "osm-storage" + type = string +} + +# tflint-ignore: terraform_unused_declarations +variable "dynamodb_table" { + description = "DynamoDB table for Terraform state locking" + default = "terraform-locks" + type = string +} + +# tflint-ignore: terraform_unused_declarations +variable "ssh_port" { + description = "Non-standard port for SSH" + default = 22 + type = number +} diff --git a/web/deploy/terraform/staging/main.tf b/web/deploy/terraform/staging/main.tf index b2eae00c..a16b3c88 100644 --- a/web/deploy/terraform/staging/main.tf +++ b/web/deploy/terraform/staging/main.tf @@ -69,34 +69,3 @@ resource "aws_eip_association" "staging" { instance_id = aws_instance.staging.id allocation_id = aws_eip.staging.id } - -output "vpc_id" { - value = module.shared_resources.vpc_id -} -output "internet_gateway_id" { - value = module.shared_resources.internet_gateway_id -} -output "route_table_id" { - value = module.shared_resources.route_table_id -} -output "network_acl_id" { - value = module.shared_resources.aws_network_acl_id -} -output "security_group_id" { - value = module.shared_resources.security_group_id -} -output "subnet_id" { - value = module.shared_resources.subnet_id -} - -output "instance_id" { - value = aws_instance.staging.id -} - -output "public_dns" { - value = aws_eip.staging.public_dns -} - -output "public_ip" { - value = aws_eip.staging.public_ip -} diff --git a/web/deploy/terraform/staging/outputs.tf b/web/deploy/terraform/staging/outputs.tf new file mode 100644 index 00000000..74805845 --- /dev/null +++ b/web/deploy/terraform/staging/outputs.tf @@ -0,0 +1,35 @@ +output "vpc_id" { + value = module.shared_resources.vpc_id +} + +output "internet_gateway_id" { + value = module.shared_resources.internet_gateway_id +} + +output "route_table_id" { + value = module.shared_resources.route_table_id +} + +output "network_acl_id" { + value = module.shared_resources.aws_network_acl_id +} + +output "security_group_id" { + value = module.shared_resources.security_group_id +} + +output "subnet_id" { + value = module.shared_resources.subnet_id +} + +output "instance_id" { + value = aws_instance.staging.id +} + +output "public_dns" { + value = aws_eip.staging.public_dns +} + +output "public_ip" { + value = aws_eip.staging.public_ip +} diff --git a/web/deploy/terraform/state/.terraform.lock.hcl b/web/deploy/terraform/state/.terraform.lock.hcl new file mode 100644 index 00000000..f324fcc5 --- /dev/null +++ b/web/deploy/terraform/state/.terraform.lock.hcl @@ -0,0 +1,20 @@ +# This file is maintained automatically by "tofu init". +# Manual edits may be lost in future updates. + +provider "registry.opentofu.org/hashicorp/aws" { + version = "5.68.0" + constraints = "~> 5.0" + hashes = [ + "h1:VMfgVqBZ6PPm6vIk0z1jHKX8SHK+/x4IfbOkZhaD6p4=", + "zh:0501ccb379b74832366860699ca6d5993b164ec44314a054453877d39c384869", + "zh:315b4eb957f84ce5580fed31e4b99b25d41634832a6939cd016fb0c4963164c9", + "zh:31defa4c379a4f1761504617824bae1b5efc93f456f055f85d1131676433085d", + "zh:3702a13f06369ee90eea413ec32db6ffa9c59648b3545301f9917f6774a840cb", + "zh:7c524cb809267ec68dd67124aa8d9fbab7722814fa875b1306d527f71b8b3bea", + "zh:ab37ec8b17be8062d804c17f5f4ddd9deaf50b3a48e6c0b979b60ef80f85192b", + "zh:baaf2c46edfe596f085f0f8f389e908a874e45c42ea5e5d5f24de1dbfed7542e", + "zh:cb37278073ede7b5e18116faebea49d5d47496d5093cec6c69065fb9ad1f622d", + "zh:ec4b64d66470b078162c13479446ad6819c93099149b478f43d990702f937fd3", + "zh:f55c3a3ba975ecfe73c729a085efb0432c02c74e91edaf40d351cdb231c3836b", + ] +} diff --git a/web/deploy/terraform/state/deprecated/README.md b/web/deploy/terraform/state/deprecated/README.md new file mode 100644 index 00000000..90299444 --- /dev/null +++ b/web/deploy/terraform/state/deprecated/README.md @@ -0,0 +1,3 @@ +The files in this directory are deprecated and only included for reference. + +This directory might be removed in the future diff --git a/web/deploy/terraform/state_storage/dynamodb-policy.json b/web/deploy/terraform/state/deprecated/dynamodb-policy.json similarity index 100% rename from web/deploy/terraform/state_storage/dynamodb-policy.json rename to web/deploy/terraform/state/deprecated/dynamodb-policy.json diff --git a/web/deploy/terraform/state_storage/README.md b/web/deploy/terraform/state/deprecated/manual-bucket-creation.md similarity index 100% rename from web/deploy/terraform/state_storage/README.md rename to web/deploy/terraform/state/deprecated/manual-bucket-creation.md diff --git a/web/deploy/terraform/state/main.tf b/web/deploy/terraform/state/main.tf new file mode 100644 index 00000000..463189bc --- /dev/null +++ b/web/deploy/terraform/state/main.tf @@ -0,0 +1,18 @@ +terraform { + required_version = ">= 1.0.0, < 2.0.0" +} + +module "stage_state" { + source = "./modules/state/" + environment = "stage" +} + +module "prod_state" { + source = "./modules/state/" + environment = "prod" +} + +module "shared_state" { + source = "./modules/state/" + environment = "shared" +} diff --git a/web/deploy/terraform/state/modules/state/main.tf b/web/deploy/terraform/state/modules/state/main.tf new file mode 100644 index 00000000..399590cc --- /dev/null +++ b/web/deploy/terraform/state/modules/state/main.tf @@ -0,0 +1,92 @@ +terraform { + required_version = ">= 1.0.0, < 2.0.0" + + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 5.0" + } + } +} + +provider "aws" { + region = var.aws_region +} + +resource "aws_s3_bucket" "tf_state" { + bucket = "${var.bucket_name}-${var.environment}" + + lifecycle { + prevent_destroy = true + } + + tags = { + Name = "${var.bucket_name}-${var.environment}" + } +} + +resource "aws_s3_bucket_lifecycle_configuration" "tf_state" { + bucket = aws_s3_bucket.tf_state.id + rule { + id = "tf_state_${var.environment}" + status = "Enabled" + + transition { + days = 30 + storage_class = "STANDARD_IA" + } + + expiration { + days = 365 + } + } + + lifecycle { + prevent_destroy = true + } +} + +resource "aws_s3_bucket_versioning" "enabled" { + bucket = aws_s3_bucket.tf_state.id + + versioning_configuration { + status = "Enabled" + } + + lifecycle { + prevent_destroy = true + } +} + +resource "aws_s3_bucket_server_side_encryption_configuration" "default" { + bucket = aws_s3_bucket.tf_state.id + + rule { + apply_server_side_encryption_by_default { + sse_algorithm = "AES256" + } + } + + lifecycle { + prevent_destroy = true + } +} + +resource "aws_dynamodb_table" "tf_locks" { + name = "${var.table_name}-${var.environment}" + billing_mode = "PAY_PER_REQUEST" + hash_key = "LockID" + + attribute { + name = "LockID" + type = "S" + } + + lifecycle { + prevent_destroy = true + } + + tags = { + Name = "${var.bucket_name}-${var.environment}" + } +} diff --git a/web/deploy/terraform/state/modules/state/outputs.tf b/web/deploy/terraform/state/modules/state/outputs.tf new file mode 100644 index 00000000..660848bd --- /dev/null +++ b/web/deploy/terraform/state/modules/state/outputs.tf @@ -0,0 +1,9 @@ +output "s3_bucket_arn" { + value = aws_s3_bucket.tf_state.arn + description = "The ARN of the S3 bucket" +} + +output "dynamodb_table_name" { + value = aws_dynamodb_table.tf_locks.name + description = "The name of the DynamoDB table" +} diff --git a/web/deploy/terraform/state/modules/state/variables.tf b/web/deploy/terraform/state/modules/state/variables.tf new file mode 100644 index 00000000..3fef1596 --- /dev/null +++ b/web/deploy/terraform/state/modules/state/variables.tf @@ -0,0 +1,22 @@ +variable "bucket_name" { + description = "The name of the S3 bucket to store Terraform state. Must be globally unique." + type = string + default = "osm-terraform-state-storage" +} + +variable "table_name" { + description = "The name of the DynamoDB table. Must be unique in this AWS account." + type = string + default = "terraform-state-locks" +} + +variable "aws_region" { + description = "The AWS region used by the deployment" + type = string + default = "us-east-1" +} + +variable "environment" { + description = "The name of the development environment. Usually `stage` or `prod`." + type = string +} diff --git a/web/deploy/terraform/state_storage/state-storage.tf b/web/deploy/terraform/state_storage/state-storage.tf deleted file mode 100644 index 9970bcaa..00000000 --- a/web/deploy/terraform/state_storage/state-storage.tf +++ /dev/null @@ -1,57 +0,0 @@ -terraform { - required_version = ">= 1.0.0, < 2.0.0" - - required_providers { - aws = { - source = "hashicorp/aws" - version = "~> 5.0" - } - } -} - -provider "aws" { - region = "us-east-1" -} - -resource "aws_s3_bucket" "tf_state" { - bucket = "osm-storage" - versioning { - enabled = true - } - server_side_encryption_configuration { - rule { - apply_server_side_encryption_by_default { - sse_algorithm = "AES256" - } - } - } - lifecycle_rule { - id = "tf_state" - enabled = true - transition { - days = 30 - storage_class = "STANDARD_IA" - } - expiration { - days = 365 - } - } - tags = { - Name = "terraform-state-storage" - } -} - -resource "aws_dynamodb_table" "tf_locks" { - name = "terraform-locks" - billing_mode = "PAY_PER_REQUEST" - hash_key = "LockID" - - attribute { - name = "LockID" - type = "S" - } - - tags = { - Name = "terraform-state-locks" - } -}