diff --git a/terraform/.gitignore b/terraform/.gitignore
index 487e9e5..1ff4199 100644
--- a/terraform/.gitignore
+++ b/terraform/.gitignore
@@ -2,3 +2,6 @@
backend.tf
.terraform*
*.swp
+*tfvars
+credentials.json
+*.pem
diff --git a/terraform/aws/examples/alb/main.tf b/terraform/aws/examples/alb/main.tf
new file mode 100644
index 0000000..d2e1940
--- /dev/null
+++ b/terraform/aws/examples/alb/main.tf
@@ -0,0 +1,75 @@
+data "aws_vpc" "mng_intranet" {
+ tags = {
+ Name = "mng-vpc-intranet01"
+ }
+}
+
+data "aws_subnet" "trusted_a" {
+ vpc_id = data.aws_vpc.mng_intranet.id
+
+ tags = {
+ Name = "mng-vpc-subnet-trusteda01"
+ }
+}
+
+data "aws_subnet" "trusted_c" {
+ vpc_id = data.aws_vpc.mng_intranet.id
+
+ tags = {
+ Name = "mng-vpc-subnet-trustedc01"
+ }
+}
+
+data "aws_security_group" "alb" {
+ filter {
+ name = "tag:Name"
+ values = ["mng-vpc-sg-default01"]
+ }
+
+ vpc_id = data.aws_vpc.mng_intranet.id
+}
+
+data "aws_acm_certificate" "this" {
+ domain = "cloudmigration-poc.internal"
+ most_recent = true
+ statuses = ["ISSUED"]
+}
+
+module "alb" {
+ source = "../../modules/alb"
+
+ name = "mng-alb-cloudmigration-poc01"
+ vpc_id = data.aws_vpc.mng_intranet.id
+
+ subnet_ids = [
+ data.aws_subnet.trusted_a.id,
+ data.aws_subnet.trusted_c.id,
+ ]
+
+ security_group_ids = [
+ data.aws_security_group.alb.id,
+ ]
+
+ idle_timeout = 60
+
+ target_group_name = "cloudmigration-poc-tg01"
+ target_group_port = 8080
+ target_type = "ip"
+
+ health_check_path = "/health"
+ health_check_healthy_threshold = 3
+ health_check_unhealthy_threshold = 3
+ health_check_timeout = 5
+ health_check_interval = 30
+ health_check_matcher = "200"
+
+ ssl_policy = "ELBSecurityPolicy-TLS-1-2-2017-01"
+ certificate_arn = data.aws_acm_certificate.this.arn
+
+ tags = {
+ ManagedBy = "terraform"
+ Project = "CloudMigration-PoC"
+ Environment = "Type-Int"
+ }
+}
+
diff --git a/terraform/aws/examples/alb/provider.tf b/terraform/aws/examples/alb/provider.tf
new file mode 100644
index 0000000..aa15b40
--- /dev/null
+++ b/terraform/aws/examples/alb/provider.tf
@@ -0,0 +1,22 @@
+terraform {
+ required_version = ">= 1.0.0"
+}
+
+provider "aws" {
+ region = var.region
+
+ default_tags {
+ tags = {
+ ManagedBy = "terraform"
+ Project = "CloudMigration-PoC"
+ Environment = "Type-Int"
+ }
+ }
+}
+
+variable "region" {
+ type = string
+ description = "The default region to use"
+ default = "ap-northeast-1"
+}
+
diff --git a/terraform/aws/examples/cloudwatch-log-groups/main.tf b/terraform/aws/examples/cloudwatch-log-groups/main.tf
new file mode 100644
index 0000000..1ad8ddd
--- /dev/null
+++ b/terraform/aws/examples/cloudwatch-log-groups/main.tf
@@ -0,0 +1,23 @@
+data "aws_kms_key" "logs" {
+ key_id = "alias/mng-kms-logs01"
+}
+
+module "cloudwatch_log_groups" {
+ source = "../../modules/cloudwatch-log-groups"
+
+ log_group_names = [
+ "/ecs/cloudmigration-poc-app",
+ "/rds/aurora-postgres/cloudmigration-poc",
+ "/dms/cloudmigration-poc",
+ ]
+
+ kms_key_arn = data.aws_kms_key.logs.arn
+ retention_in_days = 30
+
+ tags = {
+ ManagedBy = "terraform"
+ Project = "CloudMigration-PoC"
+ Environment = "Type-Int"
+ }
+}
+
diff --git a/terraform/aws/examples/cloudwatch-log-groups/provider.tf b/terraform/aws/examples/cloudwatch-log-groups/provider.tf
new file mode 100644
index 0000000..aa15b40
--- /dev/null
+++ b/terraform/aws/examples/cloudwatch-log-groups/provider.tf
@@ -0,0 +1,22 @@
+terraform {
+ required_version = ">= 1.0.0"
+}
+
+provider "aws" {
+ region = var.region
+
+ default_tags {
+ tags = {
+ ManagedBy = "terraform"
+ Project = "CloudMigration-PoC"
+ Environment = "Type-Int"
+ }
+ }
+}
+
+variable "region" {
+ type = string
+ description = "The default region to use"
+ default = "ap-northeast-1"
+}
+
diff --git a/terraform/aws/examples/dms-migration/main.tf b/terraform/aws/examples/dms-migration/main.tf
new file mode 100644
index 0000000..5b2607f
--- /dev/null
+++ b/terraform/aws/examples/dms-migration/main.tf
@@ -0,0 +1,95 @@
+data "aws_kms_key" "dms" {
+ key_id = "alias/mng-kms-dms01"
+}
+
+data "aws_dms_replication_subnet_group" "trusted" {
+ replication_subnet_group_id = "mng-dms-subnet-group-trusted01"
+}
+
+data "aws_security_group" "dms" {
+ filter {
+ name = "tag:Name"
+ values = ["mng-vpc-sg-default01"]
+ }
+}
+
+locals {
+ table_mappings = jsonencode({
+ rules = [
+ {
+ rule-type = "selection"
+ rule-id = "1"
+ rule-name = "include-all"
+ object-locator = {
+ schema-name = "%"
+ table-name = "%"
+ }
+ rule-action = "include"
+ }
+ ]
+ })
+
+ task_settings = jsonencode({
+ FullLoadSettings = {
+ TargetTablePrepMode = "DO_NOTHING"
+ }
+ })
+}
+
+# DMS migration: on-prem PostgreSQL (source) -> Aurora PostgreSQL in AWS (target).
+# Endpoints are created by the module from the connection details below.
+module "dms_migration" {
+ source = "../../modules/dms-migration"
+
+ replication_instance_id = "mng-dms-replication-instance01"
+ replication_instance_class = "dms.t3.medium"
+
+ allocated_storage = 100
+ engine_version = "3.5.0"
+ multi_az = false
+
+ replication_subnet_group_id = data.aws_dms_replication_subnet_group.trusted.replication_subnet_group_id
+ vpc_security_group_ids = [data.aws_security_group.dms.id]
+
+ kms_key_arn = data.aws_kms_key.dms.arn
+
+ maintenance_window = "sun:04:00-sun:05:00"
+ auto_minor_version_upgrade = true
+ allow_major_version_upgrade = false
+ apply_immediately = false
+
+ replication_task_id = "mng-dms-task-cloudmigration-poc01"
+ migration_type = "full-load-and-cdc"
+ table_mappings = local.table_mappings
+ replication_task_settings = local.task_settings
+
+ # Source: on-prem PostgreSQL
+ source_endpoint_id = "mng-dms-endpoint-source-onprem-postgres01"
+ source_engine_name = "postgres"
+ source_server_name = "onprem-db.example.internal"
+ source_port = 5432
+ source_database_name = "myapp"
+ source_username = "dms_user"
+ source_password = "ChangeMeOnPrem123"
+ source_ssl_mode = "require"
+ source_extra_connection_attributes = ""
+
+ # Target: Aurora PostgreSQL in AWS (use your Aurora cluster endpoint in production)
+ target_endpoint_id = "mng-dms-endpoint-target-aurora01"
+ target_engine_name = "aurora-postgresql"
+ target_server_name = "mng-aurora-postgres-cloudmigration-poc01.xxxxx.ap-northeast-1.rds.amazonaws.com"
+ target_port = 5432
+ target_database_name = "cloudmigration"
+ target_username = "cloudmigration_admin"
+ target_password = "ChangeMeAurora123"
+ target_ssl_mode = "require"
+ target_extra_connection_attributes = ""
+
+ tags = {
+ ManagedBy = "terraform"
+ Project = "CloudMigration-PoC"
+ Environment = "Type-Int"
+ }
+}
+
+
diff --git a/terraform/aws/examples/dms-migration/provider.tf b/terraform/aws/examples/dms-migration/provider.tf
new file mode 100644
index 0000000..aa15b40
--- /dev/null
+++ b/terraform/aws/examples/dms-migration/provider.tf
@@ -0,0 +1,22 @@
+terraform {
+ required_version = ">= 1.0.0"
+}
+
+provider "aws" {
+ region = var.region
+
+ default_tags {
+ tags = {
+ ManagedBy = "terraform"
+ Project = "CloudMigration-PoC"
+ Environment = "Type-Int"
+ }
+ }
+}
+
+variable "region" {
+ type = string
+ description = "The default region to use"
+ default = "ap-northeast-1"
+}
+
diff --git a/terraform/aws/examples/ecr-repository/main.tf b/terraform/aws/examples/ecr-repository/main.tf
new file mode 100644
index 0000000..a5a0fbb
--- /dev/null
+++ b/terraform/aws/examples/ecr-repository/main.tf
@@ -0,0 +1,20 @@
+data "aws_kms_key" "ecr" {
+ key_id = "alias/mng-kms-ecr01"
+}
+
+module "ecr_repository" {
+ source = "../../modules/ecr-repository"
+
+ name = "mng-ecr-cloudmigration-poc-app01"
+ kms_key_arn = data.aws_kms_key.ecr.arn
+
+ scan_on_push = true
+ image_tag_mutability = "IMMUTABLE"
+
+ tags = {
+ ManagedBy = "terraform"
+ Project = "CloudMigration-PoC"
+ Environment = "Type-Int"
+ }
+}
+
diff --git a/terraform/aws/examples/ecr-repository/provider.tf b/terraform/aws/examples/ecr-repository/provider.tf
new file mode 100644
index 0000000..aa15b40
--- /dev/null
+++ b/terraform/aws/examples/ecr-repository/provider.tf
@@ -0,0 +1,22 @@
+terraform {
+ required_version = ">= 1.0.0"
+}
+
+provider "aws" {
+ region = var.region
+
+ default_tags {
+ tags = {
+ ManagedBy = "terraform"
+ Project = "CloudMigration-PoC"
+ Environment = "Type-Int"
+ }
+ }
+}
+
+variable "region" {
+ type = string
+ description = "The default region to use"
+ default = "ap-northeast-1"
+}
+
diff --git a/terraform/aws/examples/ecs-cluster/main.tf b/terraform/aws/examples/ecs-cluster/main.tf
new file mode 100644
index 0000000..53f08ef
--- /dev/null
+++ b/terraform/aws/examples/ecs-cluster/main.tf
@@ -0,0 +1,14 @@
+module "ecs_cluster" {
+ source = "../../modules/ecs-cluster"
+
+ name = "mng-ecs-cluster-fargate01"
+
+ enable_container_insights = true
+
+ tags = {
+ ManagedBy = "terraform"
+ Project = "CloudMigration-PoC"
+ Environment = "Type-Int"
+ }
+}
+
diff --git a/terraform/aws/examples/ecs-cluster/provider.tf b/terraform/aws/examples/ecs-cluster/provider.tf
new file mode 100644
index 0000000..aa15b40
--- /dev/null
+++ b/terraform/aws/examples/ecs-cluster/provider.tf
@@ -0,0 +1,22 @@
+terraform {
+ required_version = ">= 1.0.0"
+}
+
+provider "aws" {
+ region = var.region
+
+ default_tags {
+ tags = {
+ ManagedBy = "terraform"
+ Project = "CloudMigration-PoC"
+ Environment = "Type-Int"
+ }
+ }
+}
+
+variable "region" {
+ type = string
+ description = "The default region to use"
+ default = "ap-northeast-1"
+}
+
diff --git a/terraform/aws/examples/rds-postgres/main.tf b/terraform/aws/examples/rds-postgres/main.tf
new file mode 100644
index 0000000..a302a0e
--- /dev/null
+++ b/terraform/aws/examples/rds-postgres/main.tf
@@ -0,0 +1,38 @@
+module "rds_postgres" {
+ source = "../../modules/rds-postgres"
+
+ identifier = "mng-aurora-postgres-cloudmigration-poc01"
+
+ engine_version = "16.5"
+ engine_mode = "provisioned"
+
+ instance_class = "db.t4g.micro"
+ instance_count = 2
+
+ db_name = "cloudmigration"
+ username = "cloudmigration_admin"
+ password = "ChangeMe123!"
+
+ subnet_ids = ["subnet-123", "subnet-234"]
+ vpc_security_group_ids = ["sec-grp-5432"]
+
+ kms_key_id = "arn:aws:kms:ap-northeast-1:123456789012:key/key-id"
+
+ backup_retention_period = 7
+ backup_window = "03:00-04:00"
+ maintenance_window = "sun:04:00-sun:05:00"
+ multi_az = false
+
+ deletion_protection = true
+ skip_final_snapshot = false
+ final_snapshot_identifier = "mng-aurora-postgres-cloudmigration-poc01-final"
+
+ auto_minor_version_upgrade = true
+ apply_immediately = false
+
+ tags = {
+ ManagedBy = "terraform"
+ Project = "CloudMigration-PoC"
+ Environment = "Type-Int"
+ }
+}
diff --git a/terraform/aws/examples/rds-postgres/provider.tf b/terraform/aws/examples/rds-postgres/provider.tf
new file mode 100644
index 0000000..aa15b40
--- /dev/null
+++ b/terraform/aws/examples/rds-postgres/provider.tf
@@ -0,0 +1,22 @@
+terraform {
+ required_version = ">= 1.0.0"
+}
+
+provider "aws" {
+ region = var.region
+
+ default_tags {
+ tags = {
+ ManagedBy = "terraform"
+ Project = "CloudMigration-PoC"
+ Environment = "Type-Int"
+ }
+ }
+}
+
+variable "region" {
+ type = string
+ description = "The default region to use"
+ default = "ap-northeast-1"
+}
+
diff --git a/terraform/aws/examples/rds/main.tf b/terraform/aws/examples/rds/main.tf
index fe0b323..22ae690 100644
--- a/terraform/aws/examples/rds/main.tf
+++ b/terraform/aws/examples/rds/main.tf
@@ -10,16 +10,16 @@ module "aws-vpc" {
}
module "aws-rds" {
- source = "../../modules/rds"
- vpc_id = module.aws-vpc.vpc_id
- subnet_ids = module.aws-vpc.private_subnet_ids
- engine = "mysql"
- engine_version = "8.0.33"
- instance_class = "db.t3.micro"
- database_user = "QB"
- database_port = 3306
- allocated_storage = 5
- db-identifier = "mysql"
-
+ source = "../../modules/rds"
+ vpc_id = module.aws-vpc.vpc_id
+ subnet_ids = module.aws-vpc.private_subnet_ids
+ engine = "mysql"
+ engine_version = "8.0.33"
+ instance_class = "db.t3.micro"
+ database_user = "QB"
+ database_port = 3306
+ allocated_storage = 5
+ db-identifier = "mysql"
+
}
diff --git a/terraform/aws/modules/alb/README.md b/terraform/aws/modules/alb/README.md
new file mode 100644
index 0000000..970d7b7
--- /dev/null
+++ b/terraform/aws/modules/alb/README.md
@@ -0,0 +1,34 @@
+# AWS Internal ALB Module
+
+This module creates an internal AWS Application Load Balancer (ALB) with an HTTPS listener and a single target group. It is designed for private, trusted subnets in a governed landing zone, where security groups and certificates are managed centrally.
+
+## Inputs
+
+| Name | Description | Type | Default | Required |
+|------|-------------|------|---------|:--------:|
+| [certificate_arn](#input_certificate_arn) | ARN of the ACM certificate used by the HTTPS listener. | `string` | n/a | yes |
+| [health_check_healthy_threshold](#input_health_check_healthy_threshold) | Number of consecutive health check successes before a target is considered healthy. | `number` | `3` | no |
+| [health_check_interval](#input_health_check_interval) | Interval between health checks, in seconds. | `number` | `30` | no |
+| [health_check_matcher](#input_health_check_matcher) | HTTP status code matcher for health checks. | `string` | `"200"` | no |
+| [health_check_path](#input_health_check_path) | Path for HTTP health checks. | `string` | `"/"` | no |
+| [health_check_timeout](#input_health_check_timeout) | Health check timeout, in seconds. | `number` | `5` | no |
+| [health_check_unhealthy_threshold](#input_health_check_unhealthy_threshold) | Number of consecutive health check failures before a target is considered unhealthy. | `number` | `3` | no |
+| [idle_timeout](#input_idle_timeout) | Idle timeout for the ALB, in seconds. | `number` | `60` | no |
+| [name](#input_name) | Name of the internal Application Load Balancer. | `string` | n/a | yes |
+| [security_group_ids](#input_security_group_ids) | List of existing security group IDs to associate with the ALB. | `list(string)` | n/a | yes |
+| [ssl_policy](#input_ssl_policy) | SSL policy name for the HTTPS listener. | `string` | `"ELBSecurityPolicy-TLS-1-2-2017-01"` | no |
+| [subnet_ids](#input_subnet_ids) | List of private or trusted subnet IDs where the ALB will be placed. | `list(string)` | n/a | yes |
+| [tags](#input_tags) | Map of tags to apply to ALB resources. | `map(string)` | `{}` | no |
+| [target_group_name](#input_target_group_name) | Name of the target group associated with the ALB. | `string` | n/a | yes |
+| [target_group_port](#input_target_group_port) | Port the target group forwards traffic to. | `number` | n/a | yes |
+| [target_type](#input_target_type) | Type of targets for the target group (`instance`, `ip`, or `lambda`). | `string` | `"ip"` | no |
+| [vpc_id](#input_vpc_id) | ID of the VPC where the ALB and target group are created. | `string` | n/a | yes |
+
+## Outputs
+
+| Name | Description |
+|------|-------------|
+| [alb_arn](#output_alb_arn) | ARN of the internal ALB. |
+| [alb_dns_name](#output_alb_dns_name) | DNS name of the internal ALB. |
+| [target_group_arn](#output_target_group_arn) | ARN of the associated target group. |
+
diff --git a/terraform/aws/modules/alb/main.tf b/terraform/aws/modules/alb/main.tf
new file mode 100644
index 0000000..aa1ce21
--- /dev/null
+++ b/terraform/aws/modules/alb/main.tf
@@ -0,0 +1,55 @@
+resource "aws_lb" "this" {
+ name = var.name
+ internal = true
+ load_balancer_type = "application"
+
+ subnets = var.subnet_ids
+ security_groups = var.security_group_ids
+
+ idle_timeout = var.idle_timeout
+
+ tags = merge(
+ {
+ Name = var.name
+ },
+ var.tags
+ )
+}
+
+resource "aws_lb_target_group" "this" {
+ name = var.target_group_name
+ port = var.target_group_port
+ protocol = "HTTP"
+ target_type = var.target_type
+ vpc_id = var.vpc_id
+
+ health_check {
+ path = var.health_check_path
+ healthy_threshold = var.health_check_healthy_threshold
+ unhealthy_threshold = var.health_check_unhealthy_threshold
+ timeout = var.health_check_timeout
+ interval = var.health_check_interval
+ matcher = var.health_check_matcher
+ }
+
+ tags = merge(
+ {
+ Name = var.target_group_name
+ },
+ var.tags
+ )
+}
+
+resource "aws_lb_listener" "https" {
+ load_balancer_arn = aws_lb.this.arn
+ port = 443
+ protocol = "HTTPS"
+ ssl_policy = var.ssl_policy
+ certificate_arn = var.certificate_arn
+
+ default_action {
+ type = "forward"
+ target_group_arn = aws_lb_target_group.this.arn
+ }
+}
+
diff --git a/terraform/aws/modules/alb/outputs.tf b/terraform/aws/modules/alb/outputs.tf
new file mode 100644
index 0000000..5954472
--- /dev/null
+++ b/terraform/aws/modules/alb/outputs.tf
@@ -0,0 +1,15 @@
+output "alb_arn" {
+ description = "ARN of the internal Application Load Balancer."
+ value = aws_lb.this.arn
+}
+
+output "alb_dns_name" {
+ description = "DNS name of the internal Application Load Balancer."
+ value = aws_lb.this.dns_name
+}
+
+output "target_group_arn" {
+ description = "ARN of the target group associated with the ALB."
+ value = aws_lb_target_group.this.arn
+}
+
diff --git a/terraform/aws/modules/alb/variables.tf b/terraform/aws/modules/alb/variables.tf
new file mode 100644
index 0000000..5493f3a
--- /dev/null
+++ b/terraform/aws/modules/alb/variables.tf
@@ -0,0 +1,95 @@
+variable "name" {
+ description = "Name of the internal Application Load Balancer."
+ type = string
+}
+
+variable "vpc_id" {
+ description = "ID of the VPC where the ALB and target group are created."
+ type = string
+}
+
+variable "subnet_ids" {
+ description = "List of private or trusted subnet IDs for the ALB. No public subnets should be used."
+ type = list(string)
+}
+
+variable "security_group_ids" {
+ description = "List of existing security group IDs to associate with the ALB. Security groups are managed externally; this module does not create them."
+ type = list(string)
+}
+
+variable "idle_timeout" {
+ description = "Idle timeout for the ALB in seconds."
+ type = number
+ default = 60
+}
+
+variable "target_group_name" {
+ description = "Name of the target group associated with the ALB."
+ type = string
+}
+
+variable "target_group_port" {
+ description = "Port that the target group forwards traffic to."
+ type = number
+}
+
+variable "target_type" {
+ description = "Type of targets registered with the target group (instance, ip, or lambda)."
+ type = string
+ default = "ip"
+}
+
+variable "health_check_path" {
+ description = "Path for HTTP health checks."
+ type = string
+ default = "/"
+}
+
+variable "health_check_healthy_threshold" {
+ description = "Number of consecutive health check successes required before considering a target healthy."
+ type = number
+ default = 3
+}
+
+variable "health_check_unhealthy_threshold" {
+ description = "Number of consecutive health check failures required before considering a target unhealthy."
+ type = number
+ default = 3
+}
+
+variable "health_check_timeout" {
+ description = "Health check timeout in seconds."
+ type = number
+ default = 5
+}
+
+variable "health_check_interval" {
+ description = "Interval between health checks in seconds."
+ type = number
+ default = 30
+}
+
+variable "health_check_matcher" {
+ description = "HTTP status code matcher for health checks (for example, 200 or 200-399)."
+ type = string
+ default = "200"
+}
+
+variable "ssl_policy" {
+ description = "Name of the SSL policy for the HTTPS listener."
+ type = string
+ default = "ELBSecurityPolicy-TLS-1-2-2017-01"
+}
+
+variable "certificate_arn" {
+ description = "ARN of the ACM certificate to use for the HTTPS listener."
+ type = string
+}
+
+variable "tags" {
+ description = "Map of tags to apply to ALB resources."
+ type = map(string)
+ default = {}
+}
+
diff --git a/terraform/aws/modules/cloudwatch-log-groups/README.md b/terraform/aws/modules/cloudwatch-log-groups/README.md
new file mode 100644
index 0000000..36986eb
--- /dev/null
+++ b/terraform/aws/modules/cloudwatch-log-groups/README.md
@@ -0,0 +1,19 @@
+# AWS CloudWatch Log Groups Module
+
+This module creates one or more AWS CloudWatch log groups with KMS encryption enabled and a configurable retention period. It is intended for landing zones that require encrypted logs and centrally managed retention.
+
+## Inputs
+
+| Name | Description | Type | Default | Required |
+|------|-------------|------|---------|:--------:|
+| [kms_key_arn](#input_kms_key_arn) | ARN of the KMS key used to encrypt log groups at rest. Key policy must allow CloudWatch Logs service principal (`logs.region.amazonaws.com`). | `string` | n/a | yes |
+| [log_group_names](#input_log_group_names) | List of CloudWatch log group names to create. | `list(string)` | n/a | yes |
+| [retention_in_days](#input_retention_in_days) | Number of days to retain logs. | `number` | `30` | no |
+| [tags](#input_tags) | Map of tags to apply to log groups. | `map(string)` | `{}` | no |
+
+## Outputs
+
+| Name | Description |
+|------|-------------|
+| [log_group_names](#output_log_group_names) | Names of the CloudWatch log groups created. |
+
diff --git a/terraform/aws/modules/cloudwatch-log-groups/main.tf b/terraform/aws/modules/cloudwatch-log-groups/main.tf
new file mode 100644
index 0000000..dec7df4
--- /dev/null
+++ b/terraform/aws/modules/cloudwatch-log-groups/main.tf
@@ -0,0 +1,15 @@
+resource "aws_cloudwatch_log_group" "this" {
+ for_each = toset(var.log_group_names)
+
+ name = each.value
+ retention_in_days = var.retention_in_days
+ kms_key_id = var.kms_key_arn
+
+ tags = merge(
+ {
+ Name = each.value
+ },
+ var.tags
+ )
+}
+
diff --git a/terraform/aws/modules/cloudwatch-log-groups/outputs.tf b/terraform/aws/modules/cloudwatch-log-groups/outputs.tf
new file mode 100644
index 0000000..0c4925d
--- /dev/null
+++ b/terraform/aws/modules/cloudwatch-log-groups/outputs.tf
@@ -0,0 +1,5 @@
+output "log_group_names" {
+ description = "Names of the CloudWatch log groups created."
+ value = keys(aws_cloudwatch_log_group.this)
+}
+
diff --git a/terraform/aws/modules/cloudwatch-log-groups/variables.tf b/terraform/aws/modules/cloudwatch-log-groups/variables.tf
new file mode 100644
index 0000000..af0ebba
--- /dev/null
+++ b/terraform/aws/modules/cloudwatch-log-groups/variables.tf
@@ -0,0 +1,22 @@
+variable "log_group_names" {
+ description = "List of CloudWatch log group names to create."
+ type = list(string)
+}
+
+variable "kms_key_arn" {
+ description = "ARN of the KMS key used to encrypt log groups at rest. Key policy must allow CloudWatch Logs service principal (logs.region.amazonaws.com)."
+ type = string
+}
+
+variable "retention_in_days" {
+ description = "Number of days to retain logs in CloudWatch."
+ type = number
+ default = 30
+}
+
+variable "tags" {
+ description = "Map of tags to apply to the log groups."
+ type = map(string)
+ default = {}
+}
+
diff --git a/terraform/aws/modules/dms-migration/README.md b/terraform/aws/modules/dms-migration/README.md
new file mode 100644
index 0000000..3bae12e
--- /dev/null
+++ b/terraform/aws/modules/dms-migration/README.md
@@ -0,0 +1,56 @@
+# AWS DMS Migration Module
+
+This module creates an AWS Database Migration Service (DMS) replication instance and a replication task. You can supply an existing DMS replication subnet group or have the module create one from `subnet_ids`. You can supply existing source and target endpoint ARNs or have the module create endpoints from connection details (server, port, database, credentials). It is designed for tightly governed environments where security groups are managed externally and public access is not permitted.
+
+## Inputs
+
+| Name | Description | Type | Default | Required |
+|------|-------------|------|---------|:--------:|
+| [allocated_storage](#input_allocated_storage) | Allocated storage in GiB for the DMS replication instance. | `number` | `100` | no |
+| [allow_major_version_upgrade](#input_allow_major_version_upgrade) | If true, allows major engine version upgrades. | `bool` | `false` | no |
+| [apply_immediately](#input_apply_immediately) | If true, applies modifications immediately. | `bool` | `false` | no |
+| [auto_minor_version_upgrade](#input_auto_minor_version_upgrade) | If true, minor engine upgrades are applied automatically. | `bool` | `true` | no |
+| [engine_version](#input_engine_version) | DMS replication engine version. | `string` | `"3.5.0"` | no |
+| [kms_key_arn](#input_kms_key_arn) | ARN of the KMS key for the DMS replication instance storage. | `string` | n/a | yes |
+| [maintenance_window](#input_maintenance_window) | Preferred maintenance window in UTC. | `string` | `"sun:04:00-sun:05:00"` | no |
+| [migration_type](#input_migration_type) | Migration task type: `full-load`, `cdc`, or `full-load-and-cdc`. | `string` | n/a | yes |
+| [multi_az](#input_multi_az) | If true, creates a Multi-AZ replication instance. | `bool` | `false` | no |
+| [replication_instance_class](#input_replication_instance_class) | Instance class for the DMS replication instance. | `string` | n/a | yes |
+| [replication_instance_id](#input_replication_instance_id) | Identifier for the DMS replication instance. | `string` | n/a | yes |
+| [replication_subnet_group_id](#input_replication_subnet_group_id) | ID of an existing DMS replication subnet group. If empty, the module creates one using subnet_ids. | `string` | `""` | no |
+| [subnet_ids](#input_subnet_ids) | Subnet IDs for the DMS replication subnet group when the module creates it (required if replication_subnet_group_id is empty; use at least two AZs). | `list(string)` | `[]` | no |
+| [replication_task_id](#input_replication_task_id) | Identifier for the DMS replication task. | `string` | n/a | yes |
+| [replication_task_settings](#input_replication_task_settings) | JSON document for advanced task settings. | `string` | `""` | no |
+| [source_endpoint_arn](#input_source_endpoint_arn) | ARN of an existing DMS source endpoint. If empty, the module creates one using source_* variables. | `string` | `""` | no |
+| [target_endpoint_arn](#input_target_endpoint_arn) | ARN of an existing DMS target endpoint. If empty, the module creates one using target_* variables. | `string` | `""` | no |
+| [source_endpoint_id](#input_source_endpoint_id) | Identifier for the source endpoint when the module creates it. | `string` | `""` | no |
+| [source_engine_name](#input_source_engine_name) | Engine name for the source (e.g. postgres, aurora-postgresql). | `string` | `""` | no |
+| [source_server_name](#input_source_server_name) | Hostname or IP of the source database. | `string` | `""` | no |
+| [source_port](#input_source_port) | Port of the source database. | `number` | `5432` | no |
+| [source_database_name](#input_source_database_name) | Name of the source database. | `string` | `""` | no |
+| [source_username](#input_source_username) | Username for the source database. | `string` | `""` | no |
+| [source_password](#input_source_password) | Password for the source database. | `string` | `""` | no |
+| [source_ssl_mode](#input_source_ssl_mode) | SSL mode for the source: none, require, verify-ca, verify-full. | `string` | `"none"` | no |
+| [source_extra_connection_attributes](#input_source_extra_connection_attributes) | Extra connection attributes for the source endpoint. | `string` | `""` | no |
+| [target_endpoint_id](#input_target_endpoint_id) | Identifier for the target endpoint when the module creates it. | `string` | `""` | no |
+| [target_engine_name](#input_target_engine_name) | Engine name for the target (e.g. aurora-postgresql, postgres). | `string` | `""` | no |
+| [target_server_name](#input_target_server_name) | Hostname or IP of the target database. | `string` | `""` | no |
+| [target_port](#input_target_port) | Port of the target database. | `number` | `5432` | no |
+| [target_database_name](#input_target_database_name) | Name of the target database. | `string` | `""` | no |
+| [target_username](#input_target_username) | Username for the target database. | `string` | `""` | no |
+| [target_password](#input_target_password) | Password for the target database. | `string` | `""` | no |
+| [target_ssl_mode](#input_target_ssl_mode) | SSL mode for the target: none, require, verify-ca, verify-full. | `string` | `"none"` | no |
+| [target_extra_connection_attributes](#input_target_extra_connection_attributes) | Extra connection attributes for the target endpoint. | `string` | `""` | no |
+| [table_mappings](#input_table_mappings) | JSON document for table selection and transformation rules. | `string` | n/a | yes |
+| [tags](#input_tags) | Map of tags to apply to DMS resources. | `map(string)` | `{}` | no |
+| [vpc_security_group_ids](#input_vpc_security_group_ids) | List of existing VPC security group IDs for the replication instance. | `list(string)` | n/a | yes |
+
+## Outputs
+
+| Name | Description |
+|------|-------------|
+| [replication_instance_arn](#output_replication_instance_arn) | ARN of the DMS replication instance. |
+| [replication_task_arn](#output_replication_task_arn) | ARN of the DMS replication task. |
+| [replication_subnet_group_id](#output_replication_subnet_group_id) | ID of the DMS replication subnet group in use. |
+| [source_endpoint_arn](#output_source_endpoint_arn) | ARN of the DMS source endpoint in use. |
+| [target_endpoint_arn](#output_target_endpoint_arn) | ARN of the DMS target endpoint in use. |
diff --git a/terraform/aws/modules/dms-migration/endpoint.tf b/terraform/aws/modules/dms-migration/endpoint.tf
new file mode 100644
index 0000000..9853820
--- /dev/null
+++ b/terraform/aws/modules/dms-migration/endpoint.tf
@@ -0,0 +1,47 @@
+resource "aws_dms_endpoint" "source" {
+ count = var.source_endpoint_arn != "" ? 0 : 1
+
+ endpoint_id = var.source_endpoint_id
+ endpoint_type = "source"
+ engine_name = var.source_engine_name
+
+ server_name = var.source_server_name
+ port = var.source_port
+ database_name = var.source_database_name
+ username = var.source_username
+ password = var.source_password
+
+ ssl_mode = var.source_ssl_mode
+ extra_connection_attributes = var.source_extra_connection_attributes
+
+ tags = merge(
+ {
+ Name = var.source_endpoint_id
+ },
+ var.tags
+ )
+}
+
+resource "aws_dms_endpoint" "target" {
+ count = var.target_endpoint_arn != "" ? 0 : 1
+
+ endpoint_id = var.target_endpoint_id
+ endpoint_type = "target"
+ engine_name = var.target_engine_name
+
+ server_name = var.target_server_name
+ port = var.target_port
+ database_name = var.target_database_name
+ username = var.target_username
+ password = var.target_password
+
+ ssl_mode = var.target_ssl_mode
+ extra_connection_attributes = var.target_extra_connection_attributes
+
+ tags = merge(
+ {
+ Name = var.target_endpoint_id
+ },
+ var.tags
+ )
+}
diff --git a/terraform/aws/modules/dms-migration/main.tf b/terraform/aws/modules/dms-migration/main.tf
new file mode 100644
index 0000000..8b6967a
--- /dev/null
+++ b/terraform/aws/modules/dms-migration/main.tf
@@ -0,0 +1,39 @@
+resource "aws_dms_replication_instance" "this" {
+ replication_instance_id = var.replication_instance_id
+ replication_instance_class = var.replication_instance_class
+ allocated_storage = var.allocated_storage
+ engine_version = var.engine_version
+ multi_az = var.multi_az
+ publicly_accessible = false
+ auto_minor_version_upgrade = var.auto_minor_version_upgrade
+ allow_major_version_upgrade = var.allow_major_version_upgrade
+ apply_immediately = var.apply_immediately
+ replication_subnet_group_id = var.replication_subnet_group_id != "" ? var.replication_subnet_group_id : aws_dms_replication_subnet_group.this[0].replication_subnet_group_id
+ vpc_security_group_ids = var.vpc_security_group_ids
+ kms_key_arn = var.kms_key_arn
+ preferred_maintenance_window = var.maintenance_window
+ tags = merge(
+ {
+ Name = var.replication_instance_id
+ },
+ var.tags
+ )
+}
+
+resource "aws_dms_replication_task" "this" {
+ replication_task_id = var.replication_task_id
+ migration_type = var.migration_type
+ replication_instance_arn = aws_dms_replication_instance.this.replication_instance_arn
+ source_endpoint_arn = var.source_endpoint_arn != "" ? var.source_endpoint_arn : aws_dms_endpoint.source[0].endpoint_arn
+ target_endpoint_arn = var.target_endpoint_arn != "" ? var.target_endpoint_arn : aws_dms_endpoint.target[0].endpoint_arn
+ table_mappings = var.table_mappings
+ replication_task_settings = var.replication_task_settings
+
+ tags = merge(
+ {
+ Name = var.replication_task_id
+ },
+ var.tags
+ )
+}
+
diff --git a/terraform/aws/modules/dms-migration/outputs.tf b/terraform/aws/modules/dms-migration/outputs.tf
new file mode 100644
index 0000000..37da918
--- /dev/null
+++ b/terraform/aws/modules/dms-migration/outputs.tf
@@ -0,0 +1,25 @@
+output "replication_instance_arn" {
+ description = "ARN of the DMS replication instance."
+ value = aws_dms_replication_instance.this.replication_instance_arn
+}
+
+output "replication_task_arn" {
+ description = "ARN of the DMS replication task."
+ value = aws_dms_replication_task.this.replication_task_arn
+}
+
+output "replication_subnet_group_id" {
+ description = "ID of the DMS replication subnet group in use (either provided or created by the module)."
+ value = var.replication_subnet_group_id != "" ? var.replication_subnet_group_id : aws_dms_replication_subnet_group.this[0].replication_subnet_group_id
+}
+
+output "source_endpoint_arn" {
+ description = "ARN of the DMS source endpoint (either provided or created by the module)."
+ value = var.source_endpoint_arn != "" ? var.source_endpoint_arn : aws_dms_endpoint.source[0].endpoint_arn
+}
+
+output "target_endpoint_arn" {
+ description = "ARN of the DMS target endpoint (either provided or created by the module)."
+ value = var.target_endpoint_arn != "" ? var.target_endpoint_arn : aws_dms_endpoint.target[0].endpoint_arn
+}
+
diff --git a/terraform/aws/modules/dms-migration/subnet-group.tf b/terraform/aws/modules/dms-migration/subnet-group.tf
new file mode 100644
index 0000000..153caaa
--- /dev/null
+++ b/terraform/aws/modules/dms-migration/subnet-group.tf
@@ -0,0 +1,14 @@
+resource "aws_dms_replication_subnet_group" "this" {
+ count = var.replication_subnet_group_id == "" ? 1 : 0
+
+ replication_subnet_group_id = "${var.replication_instance_id}-subnet-group"
+ replication_subnet_group_description = "DMS replication subnet group for ${var.replication_instance_id}"
+ subnet_ids = var.subnet_ids
+
+ tags = merge(
+ {
+ Name = "${var.replication_instance_id}-subnet-group"
+ },
+ var.tags
+ )
+}
diff --git a/terraform/aws/modules/dms-migration/variables.tf b/terraform/aws/modules/dms-migration/variables.tf
new file mode 100644
index 0000000..d6c55a5
--- /dev/null
+++ b/terraform/aws/modules/dms-migration/variables.tf
@@ -0,0 +1,227 @@
+variable "replication_instance_id" {
+ description = "Identifier for the DMS replication instance. Must be unique within the account and region."
+ type = string
+}
+
+variable "replication_instance_class" {
+ description = "Instance class for the DMS replication instance (for example, dms.t3.medium)."
+ type = string
+}
+
+variable "allocated_storage" {
+ description = "Amount of allocated storage in GiB for the DMS replication instance."
+ type = number
+ default = 100
+}
+
+variable "engine_version" {
+ description = "Version of the DMS replication engine to use."
+ type = string
+ default = "3.5.0"
+}
+
+variable "multi_az" {
+ description = "If true, creates a Multi-AZ DMS replication instance."
+ type = bool
+ default = false
+}
+
+variable "replication_subnet_group_id" {
+ description = "ID of an existing DMS replication subnet group. If empty, the module creates one using subnet_ids (must be in at least two AZs)."
+ type = string
+ default = ""
+}
+
+variable "subnet_ids" {
+ description = "List of subnet IDs for the DMS replication subnet group when the module creates it (required if replication_subnet_group_id is empty). Must include subnets in at least two AZs."
+ type = list(string)
+ default = []
+}
+
+variable "vpc_security_group_ids" {
+ description = "List of existing VPC security group IDs to associate with the DMS replication instance ENIs. Security groups are managed externally; this module does not create them."
+ type = list(string)
+}
+
+variable "kms_key_arn" {
+ description = "ARN of the KMS key used to encrypt DMS replication instance storage and logs."
+ type = string
+}
+
+variable "maintenance_window" {
+ description = "Preferred maintenance window in UTC for the DMS replication instance."
+ type = string
+ default = "sun:04:00-sun:05:00"
+}
+
+variable "auto_minor_version_upgrade" {
+ description = "Indicates that minor engine upgrades are applied automatically during the maintenance window."
+ type = bool
+ default = true
+}
+
+variable "allow_major_version_upgrade" {
+ description = "Indicates that major engine upgrades are allowed."
+ type = bool
+ default = false
+}
+
+variable "apply_immediately" {
+ description = "Specifies whether modifications are applied immediately or during the next maintenance window."
+ type = bool
+ default = false
+}
+
+variable "replication_task_id" {
+ description = "Identifier for the DMS replication task."
+ type = string
+}
+
+variable "migration_type" {
+ description = "Type of migration task. Valid values: full-load, cdc, full-load-and-cdc."
+ type = string
+}
+
+variable "source_endpoint_arn" {
+ description = "ARN of an existing DMS source endpoint. If empty, the module creates a source endpoint using source_* variables."
+ type = string
+ default = ""
+}
+
+variable "target_endpoint_arn" {
+ description = "ARN of an existing DMS target endpoint. If empty, the module creates a target endpoint using target_* variables."
+ type = string
+ default = ""
+}
+
+# Source endpoint (used when source_endpoint_arn is empty)
+variable "source_endpoint_id" {
+ description = "Identifier for the DMS source endpoint. Required when creating the source endpoint (source_endpoint_arn empty)."
+ type = string
+ default = ""
+}
+
+variable "source_engine_name" {
+ description = "Engine name for the source endpoint (e.g. postgres, mysql, aurora-postgresql). Required when creating the source endpoint."
+ type = string
+ default = ""
+}
+
+variable "source_server_name" {
+ description = "Hostname or IP of the source database. Required when creating the source endpoint."
+ type = string
+ default = ""
+}
+
+variable "source_port" {
+ description = "Port of the source database. Required when creating the source endpoint."
+ type = number
+ default = 5432
+}
+
+variable "source_database_name" {
+ description = "Name of the source database. Required when creating the source endpoint."
+ type = string
+ default = ""
+}
+
+variable "source_username" {
+ description = "Username for the source database. Required when creating the source endpoint."
+ type = string
+ default = ""
+ sensitive = true
+}
+
+variable "source_password" {
+ description = "Password for the source database. Required when creating the source endpoint."
+ type = string
+ default = ""
+ sensitive = true
+}
+
+variable "source_ssl_mode" {
+ description = "SSL mode for the source endpoint: none, require, verify-ca, verify-full."
+ type = string
+ default = "none"
+}
+
+variable "source_extra_connection_attributes" {
+ description = "Extra connection attributes for the source endpoint."
+ type = string
+ default = ""
+}
+
+# Target endpoint (used when target_endpoint_arn is empty)
+variable "target_endpoint_id" {
+ description = "Identifier for the DMS target endpoint. Required when creating the target endpoint (target_endpoint_arn empty)."
+ type = string
+ default = ""
+}
+
+variable "target_engine_name" {
+ description = "Engine name for the target endpoint (e.g. aurora-postgresql, postgres). Required when creating the target endpoint."
+ type = string
+ default = ""
+}
+
+variable "target_server_name" {
+ description = "Hostname or IP of the target database. Required when creating the target endpoint."
+ type = string
+ default = ""
+}
+
+variable "target_port" {
+ description = "Port of the target database. Required when creating the target endpoint."
+ type = number
+ default = 5432
+}
+
+variable "target_database_name" {
+ description = "Name of the target database. Required when creating the target endpoint."
+ type = string
+ default = ""
+}
+
+variable "target_username" {
+ description = "Username for the target database. Required when creating the target endpoint."
+ type = string
+ default = ""
+ sensitive = true
+}
+
+variable "target_password" {
+ description = "Password for the target database. Required when creating the target endpoint."
+ type = string
+ default = ""
+ sensitive = true
+}
+
+variable "target_ssl_mode" {
+ description = "SSL mode for the target endpoint: none, require, verify-ca, verify-full."
+ type = string
+ default = "none"
+}
+
+variable "target_extra_connection_attributes" {
+ description = "Extra connection attributes for the target endpoint."
+ type = string
+ default = ""
+}
+
+variable "table_mappings" {
+ description = "JSON document that specifies table selection and transformation rules for the DMS task."
+ type = string
+}
+
+variable "replication_task_settings" {
+ description = "JSON document that provides advanced settings for the DMS replication task."
+ type = string
+ default = ""
+}
+
+variable "tags" {
+ description = "Map of tags to apply to the DMS replication resources."
+ type = map(string)
+ default = {}
+}
+
diff --git a/terraform/aws/modules/ecr-repository/README.md b/terraform/aws/modules/ecr-repository/README.md
new file mode 100644
index 0000000..50bffc0
--- /dev/null
+++ b/terraform/aws/modules/ecr-repository/README.md
@@ -0,0 +1,21 @@
+# AWS ECR Repository Module
+
+This module creates an AWS Elastic Container Registry (ECR) repository with KMS encryption enabled and optional image scanning on push. It is suitable for tightly governed environments where image immutability and encryption at rest are required.
+
+## Inputs
+
+| Name | Description | Type | Default | Required |
+|------|-------------|------|---------|:--------:|
+| [image_tag_mutability](#input_image_tag_mutability) | Tag mutability setting for the repository (`MUTABLE` or `IMMUTABLE`). | `string` | `"IMMUTABLE"` | no |
+| [kms_key_arn](#input_kms_key_arn) | ARN of the KMS key used to encrypt images at rest in the ECR repository. | `string` | n/a | yes |
+| [name](#input_name) | Name of the ECR repository. | `string` | n/a | yes |
+| [scan_on_push](#input_scan_on_push) | If true, enables image scanning on push. | `bool` | `true` | no |
+| [tags](#input_tags) | Map of tags to apply to the ECR repository. | `map(string)` | `{}` | no |
+
+## Outputs
+
+| Name | Description |
+|------|-------------|
+| [repository_arn](#output_repository_arn) | ARN of the ECR repository. |
+| [repository_url](#output_repository_url) | URL of the ECR repository. |
+
diff --git a/terraform/aws/modules/ecr-repository/main.tf b/terraform/aws/modules/ecr-repository/main.tf
new file mode 100644
index 0000000..3367fa9
--- /dev/null
+++ b/terraform/aws/modules/ecr-repository/main.tf
@@ -0,0 +1,20 @@
+resource "aws_ecr_repository" "this" {
+ name = var.name
+ image_tag_mutability = var.image_tag_mutability
+
+ image_scanning_configuration {
+ scan_on_push = var.scan_on_push
+ }
+
+ encryption_configuration {
+ encryption_type = "KMS"
+ kms_key = var.kms_key_arn
+ }
+
+ tags = merge(
+ {
+ Name = var.name
+ },
+ var.tags
+ )
+}
\ No newline at end of file
diff --git a/terraform/aws/modules/ecr-repository/outputs.tf b/terraform/aws/modules/ecr-repository/outputs.tf
new file mode 100644
index 0000000..fa990ca
--- /dev/null
+++ b/terraform/aws/modules/ecr-repository/outputs.tf
@@ -0,0 +1,10 @@
+output "repository_arn" {
+ description = "ARN of the ECR repository."
+ value = aws_ecr_repository.this.arn
+}
+
+output "repository_url" {
+ description = "URL of the ECR repository."
+ value = aws_ecr_repository.this.repository_url
+}
+
diff --git a/terraform/aws/modules/ecr-repository/variables.tf b/terraform/aws/modules/ecr-repository/variables.tf
new file mode 100644
index 0000000..18748b2
--- /dev/null
+++ b/terraform/aws/modules/ecr-repository/variables.tf
@@ -0,0 +1,27 @@
+variable "name" {
+ description = "Name of the ECR repository. Must be unique within the account and region."
+ type = string
+}
+
+variable "kms_key_arn" {
+ description = "ARN of the KMS key used to encrypt images at rest in the ECR repository."
+ type = string
+}
+
+variable "scan_on_push" {
+ description = "If true, enables image scanning on push for the repository."
+ type = bool
+ default = true
+}
+
+variable "image_tag_mutability" {
+ description = "The tag mutability setting for the repository. Valid values are MUTABLE and IMMUTABLE."
+ type = string
+ default = "IMMUTABLE"
+}
+
+variable "tags" {
+ description = "Map of tags to apply to the ECR repository."
+ type = map(string)
+ default = {}
+}
\ No newline at end of file
diff --git a/terraform/aws/modules/ecs-cluster/README.md b/terraform/aws/modules/ecs-cluster/README.md
new file mode 100644
index 0000000..db045ec
--- /dev/null
+++ b/terraform/aws/modules/ecs-cluster/README.md
@@ -0,0 +1,20 @@
+# AWS ECS Cluster Module
+
+This module creates an AWS ECS cluster that can be used to run Fargate services and, in the future, EC2 Auto Scaling group-based services. It is designed to integrate into a pre-provisioned, tightly governed landing zone where networking and security controls are managed centrally.
+
+## Inputs
+
+| Name | Description | Type | Default | Required |
+|------|-------------|------|---------|:--------:|
+| [name](#input_name) | Name of the ECS cluster to create. | `string` | n/a | yes |
+| [enable_container_insights](#input_enable_container_insights) | If true, enables CloudWatch Container Insights for the ECS cluster. | `bool` | `true` | no |
+| [tags](#input_tags) | Map of tags to apply to the ECS cluster. | `map(string)` | `{}` | no |
+
+## Outputs
+
+| Name | Description |
+|------|-------------|
+| [cluster_arn](#output_cluster_arn) | ARN of the ECS cluster. |
+| [cluster_id](#output_cluster_id) | ID of the ECS cluster. |
+| [cluster_name](#output_cluster_name) | Name of the ECS cluster. |
+
diff --git a/terraform/aws/modules/ecs-cluster/main.tf b/terraform/aws/modules/ecs-cluster/main.tf
new file mode 100644
index 0000000..384e3a2
--- /dev/null
+++ b/terraform/aws/modules/ecs-cluster/main.tf
@@ -0,0 +1,16 @@
+resource "aws_ecs_cluster" "this" {
+ name = var.name
+
+ setting {
+ name = "containerInsights"
+ value = var.enable_container_insights ? "enabled" : "disabled"
+ }
+
+ tags = merge(
+ {
+ Name = var.name
+ },
+ var.tags
+ )
+}
+
diff --git a/terraform/aws/modules/ecs-cluster/outputs.tf b/terraform/aws/modules/ecs-cluster/outputs.tf
new file mode 100644
index 0000000..1a83ee4
--- /dev/null
+++ b/terraform/aws/modules/ecs-cluster/outputs.tf
@@ -0,0 +1,15 @@
+output "cluster_id" {
+ description = "ID of the ECS cluster."
+ value = aws_ecs_cluster.this.id
+}
+
+output "cluster_arn" {
+ description = "ARN of the ECS cluster."
+ value = aws_ecs_cluster.this.arn
+}
+
+output "cluster_name" {
+ description = "Name of the ECS cluster."
+ value = aws_ecs_cluster.this.name
+}
+
diff --git a/terraform/aws/modules/ecs-cluster/variables.tf b/terraform/aws/modules/ecs-cluster/variables.tf
new file mode 100644
index 0000000..804c791
--- /dev/null
+++ b/terraform/aws/modules/ecs-cluster/variables.tf
@@ -0,0 +1,16 @@
+variable "name" {
+ description = "Name of the ECS cluster to create. Choose a name that aligns with landing zone naming conventions."
+ type = string
+}
+
+variable "enable_container_insights" {
+ description = "If true, enables CloudWatch Container Insights for the ECS cluster."
+ type = bool
+ default = true
+}
+
+variable "tags" {
+ description = "Map of tags to apply to the ECS cluster."
+ type = map(string)
+ default = {}
+}
\ No newline at end of file
diff --git a/terraform/aws/modules/kms-key/README.md b/terraform/aws/modules/kms-key/README.md
new file mode 100644
index 0000000..a21cab6
--- /dev/null
+++ b/terraform/aws/modules/kms-key/README.md
@@ -0,0 +1,22 @@
+# AWS KMS Key Module
+
+This module creates a single-region KMS key and alias. It is designed for governed environments (no multi-region, no external sharing). Optionally provide a custom key policy.
+
+## Inputs
+
+| Name | Description | Type | Default | Required |
+|------|-------------|------|---------|:--------:|
+| [alias_name](#input_alias_name) | Name for the KMS alias (without 'alias/' prefix). | `string` | n/a | yes |
+| [description](#input_description) | Description of the KMS key. | `string` | n/a | yes |
+| [deletion_window_in_days](#input_deletion_window_in_days) | Number of days to retain the key after deletion. | `number` | `7` | no |
+| [enable_key_rotation](#input_enable_key_rotation) | Whether to enable automatic key rotation. | `bool` | `false` | no |
+| [policy](#input_policy) | Key policy JSON. If null, uses default root-only policy. | `string` | `null` | no |
+| [tags](#input_tags) | Tags to apply to the KMS key. | `map(string)` | `{}` | no |
+
+## Outputs
+
+| Name | Description |
+|------|-------------|
+| [key_arn](#output_key_arn) | ARN of the KMS key. |
+| [key_id](#output_key_id) | ID of the KMS key. |
+| [alias_name](#output_alias_name) | Alias name of the KMS key. |
diff --git a/terraform/aws/modules/kms-key/main.tf b/terraform/aws/modules/kms-key/main.tf
new file mode 100644
index 0000000..03e29dd
--- /dev/null
+++ b/terraform/aws/modules/kms-key/main.tf
@@ -0,0 +1,36 @@
+data "aws_caller_identity" "current" {}
+data "aws_region" "current" {}
+
+resource "aws_kms_key" "this" {
+ description = var.description
+ deletion_window_in_days = var.deletion_window_in_days
+ enable_key_rotation = var.enable_key_rotation
+ multi_region = false
+
+ policy = var.policy != null ? var.policy : jsonencode({
+ Version = "2012-10-17"
+ Statement = [
+ {
+ Sid = "Enable IAM User Permissions"
+ Effect = "Allow"
+ Principal = {
+ AWS = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:root"
+ }
+ Action = "kms:*"
+ Resource = "*"
+ }
+ ]
+ })
+
+ tags = merge(
+ {
+ Name = var.alias_name
+ },
+ var.tags
+ )
+}
+
+resource "aws_kms_alias" "this" {
+ name = "alias/${var.alias_name}"
+ target_key_id = aws_kms_key.this.key_id
+}
diff --git a/terraform/aws/modules/kms-key/outputs.tf b/terraform/aws/modules/kms-key/outputs.tf
new file mode 100644
index 0000000..1ecf418
--- /dev/null
+++ b/terraform/aws/modules/kms-key/outputs.tf
@@ -0,0 +1,14 @@
+output "key_arn" {
+ description = "ARN of the KMS key."
+ value = aws_kms_key.this.arn
+}
+
+output "key_id" {
+ description = "ID of the KMS key."
+ value = aws_kms_key.this.key_id
+}
+
+output "alias_name" {
+ description = "Alias name of the KMS key."
+ value = aws_kms_alias.this.name
+}
diff --git a/terraform/aws/modules/kms-key/variables.tf b/terraform/aws/modules/kms-key/variables.tf
new file mode 100644
index 0000000..2437ae0
--- /dev/null
+++ b/terraform/aws/modules/kms-key/variables.tf
@@ -0,0 +1,33 @@
+variable "alias_name" {
+ description = "Name for the KMS alias (without 'alias/' prefix)."
+ type = string
+}
+
+variable "description" {
+ description = "Description of the KMS key."
+ type = string
+}
+
+variable "deletion_window_in_days" {
+ description = "Number of days to retain the key after deletion."
+ type = number
+ default = 7
+}
+
+variable "enable_key_rotation" {
+ description = "Whether to enable automatic key rotation."
+ type = bool
+ default = false
+}
+
+variable "policy" {
+ description = "Key policy JSON. If null, uses default root-only policy."
+ type = string
+ default = null
+}
+
+variable "tags" {
+ description = "Tags to apply to the KMS key."
+ type = map(string)
+ default = {}
+}
diff --git a/terraform/aws/modules/rds-postgres/README.md b/terraform/aws/modules/rds-postgres/README.md
new file mode 100644
index 0000000..760cb7c
--- /dev/null
+++ b/terraform/aws/modules/rds-postgres/README.md
@@ -0,0 +1,50 @@
+# AWS Aurora PostgreSQL Module
+
+This module creates an Amazon Aurora PostgreSQL cluster with KMS encryption at rest, private network placement, and strictly disabled public accessibility. It supports both provisioned instances and Aurora Serverless. You can supply an existing DB subnet group or let the module create one from a list of subnet IDs. Cluster and DB parameter groups are created by the module and can be customized via variables.
+
+## Inputs
+
+| Name | Description | Type | Default | Required |
+|------|-------------|------|---------|:--------:|
+| [apply_immediately](#input_apply_immediately) | If true, applies modifications immediately instead of during the next maintenance window. | `bool` | `false` | no |
+| [auto_minor_version_upgrade](#input_auto_minor_version_upgrade) | If true, minor engine upgrades are applied automatically. | `bool` | `true` | no |
+| [backup_retention_period](#input_backup_retention_period) | Number of days to retain automated backups. Set to 0 to disable. | `number` | `7` | no |
+| [backup_window](#input_backup_window) | Preferred backup window in UTC (e.g. 03:00-04:00). | `string` | `"03:00-04:00"` | no |
+| [cluster_parameters](#input_cluster_parameters) | List of cluster parameters. Each object has name, value, and optional apply_method. | `list(object)` | `[]` | no |
+| [db_name](#input_db_name) | Name of the initial database to create. | `string` | n/a | yes |
+| [db_parameters](#input_db_parameters) | List of DB instance parameters. Each object has name, value, and optional apply_method. | `list(object)` | `[]` | no |
+| [db_subnet_group_name](#input_db_subnet_group_name) | Name of an existing DB subnet group. If empty, the module creates one using subnet_ids. | `string` | `""` | no |
+| [deletion_protection](#input_deletion_protection) | If true, enables deletion protection for the Aurora cluster. | `bool` | `true` | no |
+| [engine_mode](#input_engine_mode) | Engine mode: `provisioned` or `serverless`. | `string` | `"provisioned"` | no |
+| [engine_version](#input_engine_version) | Aurora PostgreSQL engine version (e.g. 15.5). | `string` | n/a | yes |
+| [final_snapshot_identifier](#input_final_snapshot_identifier) | Identifier for the final snapshot when the cluster is destroyed (when skip_final_snapshot is false). | `string` | `""` | no |
+| [identifier](#input_identifier) | Cluster identifier for the Aurora PostgreSQL cluster. | `string` | n/a | yes |
+| [instance_class](#input_instance_class) | Instance class when engine_mode is `provisioned` (e.g. db.r6g.large). | `string` | n/a | yes |
+| [instance_count](#input_instance_count) | Number of cluster instances when engine_mode is `provisioned`. | `number` | `2` | no |
+| [kms_key_id](#input_kms_key_id) | ARN or ID of the KMS key for encrypting Aurora storage and snapshots. | `string` | n/a | yes |
+| [maintenance_window](#input_maintenance_window) | Preferred maintenance window in UTC. | `string` | `"sun:04:00-sun:05:00"` | no |
+| [multi_az](#input_multi_az) | Reserved for compatibility. | `bool` | `false` | no |
+| [password](#input_password) | Master password for the Aurora PostgreSQL cluster. | `string` | n/a | yes |
+| [region](#input_region) | AWS region (for reference; region is set by the provider). | `string` | `null` | no |
+| [skip_final_snapshot](#input_skip_final_snapshot) | If true, no final snapshot is created when the cluster is destroyed. | `bool` | `false` | no |
+| [subnet_ids](#input_subnet_ids) | Subnet IDs for the DB subnet group when the module creates it (required if db_subnet_group_name is empty). | `list(string)` | `[]` | no |
+| [serverless_auto_pause](#input_serverless_auto_pause) | Whether to auto-pause Aurora Serverless when idle. | `bool` | `false` | no |
+| [serverless_max_capacity](#input_serverless_max_capacity) | Maximum capacity (ACUs) for Aurora Serverless. | `number` | `8` | no |
+| [serverless_min_capacity](#input_serverless_min_capacity) | Minimum capacity (ACUs) for Aurora Serverless. | `number` | `2` | no |
+| [serverless_seconds_until_auto_pause](#input_serverless_seconds_until_auto_pause) | Seconds until Aurora Serverless is auto-paused when auto_pause is true. | `number` | `300` | no |
+| [tags](#input_tags) | Map of tags to apply to the Aurora cluster and its instances. | `map(string)` | `{}` | no |
+| [username](#input_username) | Master username for the Aurora PostgreSQL cluster. | `string` | n/a | yes |
+| [vpc_security_group_ids](#input_vpc_security_group_ids) | List of existing VPC security group IDs to attach to the cluster. | `list(string)` | n/a | yes |
+
+## Outputs
+
+| Name | Description |
+|------|-------------|
+| [cluster_arn](#output_cluster_arn) | ARN of the Aurora PostgreSQL cluster. |
+| [cluster_id](#output_cluster_id) | ID of the Aurora PostgreSQL cluster. |
+| [cluster_endpoint](#output_cluster_endpoint) | Writer endpoint for the Aurora PostgreSQL cluster. |
+| [cluster_reader_endpoint](#output_cluster_reader_endpoint) | Reader endpoint for the Aurora PostgreSQL cluster. |
+| [port](#output_port) | Port the database is listening on. |
+| [db_subnet_group_name](#output_db_subnet_group_name) | Name of the DB subnet group in use. |
+| [cluster_parameter_group_name](#output_cluster_parameter_group_name) | Name of the RDS cluster parameter group. |
+| [db_parameter_group_name](#output_db_parameter_group_name) | Name of the DB parameter group. |
diff --git a/terraform/aws/modules/rds-postgres/Untitled b/terraform/aws/modules/rds-postgres/Untitled
new file mode 100644
index 0000000..115c531
--- /dev/null
+++ b/terraform/aws/modules/rds-postgres/Untitled
@@ -0,0 +1 @@
+final_snapshot_identifier
\ No newline at end of file
diff --git a/terraform/aws/modules/rds-postgres/main.tf b/terraform/aws/modules/rds-postgres/main.tf
new file mode 100644
index 0000000..c753a4d
--- /dev/null
+++ b/terraform/aws/modules/rds-postgres/main.tf
@@ -0,0 +1,74 @@
+resource "aws_rds_cluster" "this" {
+ cluster_identifier = var.identifier
+
+ engine = "aurora-postgresql"
+ engine_version = var.engine_version
+ engine_mode = var.engine_mode
+
+ database_name = var.db_name
+ master_username = var.username
+ master_password = var.password
+
+ db_subnet_group_name = var.db_subnet_group_name != "" ? var.db_subnet_group_name : aws_db_subnet_group.this[0].name
+ vpc_security_group_ids = var.vpc_security_group_ids
+
+ storage_encrypted = true
+ kms_key_id = var.kms_key_id
+
+ backup_retention_period = var.backup_retention_period
+ preferred_backup_window = var.backup_window
+ preferred_maintenance_window = var.maintenance_window
+
+ deletion_protection = var.deletion_protection
+ apply_immediately = var.apply_immediately
+
+ skip_final_snapshot = var.skip_final_snapshot
+ final_snapshot_identifier = var.final_snapshot_identifier != "" ? var.final_snapshot_identifier : var.identifier
+
+ db_cluster_parameter_group_name = aws_rds_cluster_parameter_group.this.name
+
+ copy_tags_to_snapshot = true
+
+ dynamic "scaling_configuration" {
+ for_each = var.engine_mode == "serverless" ? [1] : []
+ content {
+ auto_pause = var.serverless_auto_pause
+ min_capacity = var.serverless_min_capacity
+ max_capacity = var.serverless_max_capacity
+ seconds_until_auto_pause = var.serverless_seconds_until_auto_pause
+ }
+ }
+
+ tags = merge(
+ {
+ Name = var.identifier
+ },
+ var.tags
+ )
+}
+
+resource "aws_rds_cluster_instance" "rds_instance" {
+ count = var.engine_mode == "provisioned" ? var.instance_count : 0
+
+ identifier = "${var.identifier}-${count.index + 1}"
+ cluster_identifier = aws_rds_cluster.this.id
+
+ engine = aws_rds_cluster.this.engine
+ engine_version = aws_rds_cluster.this.engine_version
+
+ instance_class = var.instance_class
+
+ publicly_accessible = false
+ db_subnet_group_name = var.db_subnet_group_name != "" ? var.db_subnet_group_name : aws_db_subnet_group.this[0].name
+ db_parameter_group_name = aws_db_parameter_group.this.name
+
+ auto_minor_version_upgrade = var.auto_minor_version_upgrade
+ apply_immediately = var.apply_immediately
+
+ tags = merge(
+ {
+ Name = "${var.identifier}-${count.index + 1}"
+ },
+ var.tags
+ )
+}
diff --git a/terraform/aws/modules/rds-postgres/outputs.tf b/terraform/aws/modules/rds-postgres/outputs.tf
new file mode 100644
index 0000000..b0f9f48
--- /dev/null
+++ b/terraform/aws/modules/rds-postgres/outputs.tf
@@ -0,0 +1,39 @@
+output "cluster_arn" {
+ description = "ARN of the Aurora PostgreSQL cluster."
+ value = aws_rds_cluster.this.arn
+}
+
+output "cluster_id" {
+ description = "ID of the Aurora PostgreSQL cluster."
+ value = aws_rds_cluster.this.id
+}
+
+output "cluster_endpoint" {
+ description = "Writer endpoint for the Aurora PostgreSQL cluster."
+ value = aws_rds_cluster.this.endpoint
+}
+
+output "cluster_reader_endpoint" {
+ description = "Reader endpoint for the Aurora PostgreSQL cluster."
+ value = aws_rds_cluster.this.reader_endpoint
+}
+
+output "port" {
+ description = "Port the database is listening on."
+ value = aws_rds_cluster.this.port
+}
+
+output "db_subnet_group_name" {
+ description = "Name of the DB subnet group."
+ value = var.db_subnet_group_name != "" ? var.db_subnet_group_name : aws_db_subnet_group.this[0].name
+}
+
+output "cluster_parameter_group_name" {
+ description = "Name of the RDS cluster parameter group."
+ value = aws_rds_cluster_parameter_group.this.name
+}
+
+output "db_parameter_group_name" {
+ description = "Name of the DB parameter group."
+ value = aws_db_parameter_group.this.name
+}
diff --git a/terraform/aws/modules/rds-postgres/parameters.tf b/terraform/aws/modules/rds-postgres/parameters.tf
new file mode 100644
index 0000000..0692a49
--- /dev/null
+++ b/terraform/aws/modules/rds-postgres/parameters.tf
@@ -0,0 +1,43 @@
+resource "aws_rds_cluster_parameter_group" "this" {
+ name = "${var.identifier}-${replace(var.engine_version, ".", "-")}"
+ family = "aurora-postgresql${split(".", var.engine_version)[0]}"
+ description = "Cluster parameter group for Aurora PostgreSQL"
+
+ dynamic "parameter" {
+ for_each = var.cluster_parameters
+ content {
+ name = parameter.value.name
+ value = parameter.value.value
+ apply_method = lookup(parameter.value, "apply_method", "immediate")
+ }
+ }
+
+ tags = merge(
+ {
+ Name = "${var.identifier}-${replace(var.engine_version, ".", "-")}"
+ },
+ var.tags
+ )
+}
+
+resource "aws_db_parameter_group" "this" {
+ name = "${var.identifier}-${replace(var.engine_version, ".", "-")}"
+ family = "aurora-postgresql${split(".", var.engine_version)[0]}"
+ description = "DB parameter group for Aurora PostgreSQL"
+
+ dynamic "parameter" {
+ for_each = var.db_parameters
+ content {
+ name = parameter.value.name
+ value = parameter.value.value
+ apply_method = lookup(parameter.value, "apply_method", "immediate")
+ }
+ }
+
+ tags = merge(
+ {
+ Name = "${var.identifier}-${replace(var.engine_version, ".", "-")}"
+ },
+ var.tags
+ )
+}
diff --git a/terraform/aws/modules/rds-postgres/subnet-group.tf b/terraform/aws/modules/rds-postgres/subnet-group.tf
new file mode 100644
index 0000000..a288555
--- /dev/null
+++ b/terraform/aws/modules/rds-postgres/subnet-group.tf
@@ -0,0 +1,12 @@
+resource "aws_db_subnet_group" "this" {
+ count = var.db_subnet_group_name == "" ? 1 : 0
+ name = var.identifier
+ subnet_ids = var.subnet_ids
+
+ tags = merge(
+ {
+ Name = var.identifier
+ },
+ var.tags
+ )
+}
diff --git a/terraform/aws/modules/rds-postgres/variables.tf b/terraform/aws/modules/rds-postgres/variables.tf
new file mode 100644
index 0000000..2653101
--- /dev/null
+++ b/terraform/aws/modules/rds-postgres/variables.tf
@@ -0,0 +1,176 @@
+variable "identifier" {
+ description = "Cluster identifier for the Aurora PostgreSQL cluster. Must be unique within the account and region."
+ type = string
+}
+
+variable "engine_version" {
+ description = "Version of the Aurora PostgreSQL engine to use (for example, 15.5)."
+ type = string
+}
+
+variable "engine_mode" {
+ description = "Engine mode for the Aurora PostgreSQL cluster. Use `provisioned` for standard instances or `serverless` for Aurora Serverless."
+ type = string
+ default = "provisioned"
+}
+
+variable "instance_class" {
+ description = "Instance class for Aurora PostgreSQL cluster instances (for example, db.r6g.large). Used when engine_mode is `provisioned`."
+ type = string
+}
+
+variable "db_name" {
+ description = "Name of the initial database to create."
+ type = string
+}
+
+variable "username" {
+ description = "Master username for the Aurora PostgreSQL cluster."
+ type = string
+}
+
+variable "password" {
+ description = "Master password for the Aurora PostgreSQL cluster."
+ type = string
+ sensitive = true
+}
+
+variable "db_subnet_group_name" {
+ description = "Name of an existing DB subnet group to associate with this RDS instance. The subnet group must reference trusted subnets only. If not set, the module will create its own subnet group"
+ type = string
+ default = ""
+}
+
+variable "vpc_security_group_ids" {
+ description = "List of existing VPC security group IDs to associate with the Aurora cluster. Security groups are managed externally; this module does not create them."
+ type = list(string)
+}
+
+variable "kms_key_id" {
+ description = "ARN or ID of the KMS key to use for encrypting the Aurora cluster storage and snapshots."
+ type = string
+}
+
+variable "backup_retention_period" {
+ description = "Number of days to retain automated backups. Set to 0 to disable automated backups."
+ type = number
+ default = 7
+}
+
+variable "backup_window" {
+ description = "Preferred backup window in UTC (for example, 03:00-04:00)."
+ type = string
+ default = "03:00-04:00"
+}
+
+variable "maintenance_window" {
+ description = "Preferred maintenance window in UTC (for example, sun:04:00-sun:05:00)."
+ type = string
+ default = "sun:04:00-sun:05:00"
+}
+
+variable "multi_az" {
+ description = "Reserved for compatibility. Aurora clusters inherently provide high availability across AZs."
+ type = bool
+ default = false
+}
+
+variable "deletion_protection" {
+ description = "If true, enables deletion protection for the Aurora cluster."
+ type = bool
+ default = true
+}
+
+variable "skip_final_snapshot" {
+ description = "Reserved for compatibility. Final snapshot behavior is controlled at the cluster level via deletion_protection and lifecycle policies."
+ type = bool
+ default = false
+}
+
+variable "final_snapshot_identifier" {
+ description = "Identifier for the final snapshot to create when the Aurora cluster is destroyed and skip_final_snapshot is false."
+ type = string
+ default = ""
+}
+
+variable "auto_minor_version_upgrade" {
+ description = "Indicates that minor engine upgrades are applied automatically to Aurora instances during the maintenance window."
+ type = bool
+ default = true
+}
+
+variable "apply_immediately" {
+ description = "Specifies whether any Aurora cluster modifications are applied immediately, or during the next maintenance window."
+ type = bool
+ default = false
+}
+
+variable "tags" {
+ description = "Map of tags to apply to the Aurora cluster and its instances."
+ type = map(string)
+ default = {}
+}
+
+variable "instance_count" {
+ description = "Number of Aurora cluster instances to create when engine_mode is `provisioned`."
+ type = number
+ default = 2
+}
+
+variable "serverless_min_capacity" {
+ description = "Minimum capacity for Aurora Serverless (ACUs). Used when engine_mode is `serverless`."
+ type = number
+ default = 2
+}
+
+variable "serverless_max_capacity" {
+ description = "Maximum capacity for Aurora Serverless (ACUs). Used when engine_mode is `serverless`."
+ type = number
+ default = 8
+}
+
+variable "serverless_auto_pause" {
+ description = "Whether to enable automatic pause for Aurora Serverless when there are no connections."
+ type = bool
+ default = false
+}
+
+variable "serverless_seconds_until_auto_pause" {
+ description = "Time in seconds before the Aurora Serverless cluster is auto-paused. Used when auto_pause is true."
+ type = number
+ default = 300
+}
+
+variable "region" {
+ description = "AWS region where the RDS cluster will be created."
+ type = string
+ default = null
+}
+
+variable "subnet_ids" {
+ description = "List of subnet IDs to use for the DB subnet group."
+ type = list(string)
+ default = []
+}
+
+variable "cluster_parameters" {
+ description = "List of cluster parameters to apply. Each parameter should have name, value, and optionally apply_method."
+ type = list(object({
+ name = string
+ value = string
+ apply_method = optional(string)
+ }))
+ default = []
+}
+
+variable "db_parameters" {
+ description = "List of DB parameters to apply. Each parameter should have name, value, and optionally apply_method."
+ type = list(object({
+ name = string
+ value = string
+ apply_method = optional(string)
+ }))
+ default = []
+}
+
+
diff --git a/terraform/aws/modules/rds/rds.tf b/terraform/aws/modules/rds/rds.tf
index 43452c3..144d008 100644
--- a/terraform/aws/modules/rds/rds.tf
+++ b/terraform/aws/modules/rds/rds.tf
@@ -1,5 +1,5 @@
locals {
-
+
subnet_ids_provided = var.subnet_ids != null && length(var.subnet_ids) > 0
db_subnet_group_name_provided = var.db_subnet_group_name != null && var.db_subnet_group_name != ""
@@ -19,15 +19,15 @@ resource "random_password" "db-password" {
override_special = "!#$%&*()-_=+[]{}<>:?"
}
-resource "aws_kms_key" "db_ssm_encrypt" {
- description = "KMS key for database parameter-store encryption"
- enable_key_rotation = true
- tags = {
- Name = "${var.tagname}-rds-ssm-kms-key"
- }
+resource "aws_kms_key" "db_ssm_encrypt" {
+ description = "KMS key for database parameter-store encryption"
+ enable_key_rotation = true
+ tags = {
+ Name = "${var.tagname}-rds-ssm-kms-key"
+ }
}
-resource "aws_ssm_parameter" "db-password" {
+resource "aws_ssm_parameter" "db-password" {
name = "/${var.tagname}/DATABASE_PASSWORD"
description = "SSM Parameter for database password"
type = "SecureString"
@@ -35,15 +35,15 @@ resource "aws_ssm_parameter" "db-password" {
key_id = aws_kms_key.db_ssm_encrypt.arn
tags = {
- Name = "${var.tagname}-dbpassword"
+ Name = "${var.tagname}-dbpassword"
}
}
resource "aws_db_instance" "default" {
- count = var.enabled ? 1 : 0
-
+ count = var.enabled ? 1 : 0
+
identifier = var.db-identifier
db_name = var.database_name
username = var.database_user
@@ -77,19 +77,19 @@ resource "aws_db_instance" "default" {
copy_tags_to_snapshot = var.copy_tags_to_snapshot
backup_retention_period = var.backup_retention_period
backup_window = var.backup_window
-
- deletion_protection = var.deletion_protection
- timezone = var.timezone
- vpc_security_group_ids = compact(
+
+ deletion_protection = var.deletion_protection
+ timezone = var.timezone
+ vpc_security_group_ids = compact(
concat(
[join("", aws_security_group.default.*.id)],
var.associate_security_group_ids
)
)
- tags = {
- Name = "${var.tagname}-rds"
- }
+ tags = {
+ Name = "${var.tagname}-rds"
+ }
iam_database_authentication_enabled = var.iam_database_authentication_enabled
@@ -121,32 +121,32 @@ resource "aws_db_instance" "default" {
}
- resource "aws_db_subnet_group" "default" {
- count = local.subnet_ids_provided && !local.db_subnet_group_name_provided ? 1 : 0
+resource "aws_db_subnet_group" "default" {
+ count = local.subnet_ids_provided && !local.db_subnet_group_name_provided ? 1 : 0
subnet_ids = var.subnet_ids
-
+
}
resource "aws_security_group" "default" {
-
+
name = "${var.tagname}-rds-security"
description = "Allow inbound traffic from the security groups"
vpc_id = var.vpc_id
- tags = {
- Name = "${var.tagname}-sg-rds"
- }
+ tags = {
+ Name = "${var.tagname}-sg-rds"
+ }
ingress {
- from_port = var.database_port
- to_port = var.database_port
- protocol = "tcp"
- cidr_blocks = ["0.0.0.0/0"]
+ from_port = var.database_port
+ to_port = var.database_port
+ protocol = "tcp"
+ cidr_blocks = ["0.0.0.0/0"]
}
egress {
- from_port = 0
- to_port = 0
- protocol = "-1"
- cidr_blocks = ["0.0.0.0/0"]
+ from_port = 0
+ to_port = 0
+ protocol = "-1"
+ cidr_blocks = ["0.0.0.0/0"]
}
}
diff --git a/terraform/aws/modules/rds/variable.tf b/terraform/aws/modules/rds/variable.tf
index 5703d1e..55f8d06 100644
--- a/terraform/aws/modules/rds/variable.tf
+++ b/terraform/aws/modules/rds/variable.tf
@@ -101,8 +101,8 @@ variable "instance_class" {
}
variable "db_subnet_group_name" {
- type = string
- default = null
+ type = string
+ default = null
}
variable "db_parameter_group" {
@@ -254,7 +254,7 @@ variable "iam_database_authentication_enabled" {
description = "Specifies whether or mappings of AWS Identity and Access Management (IAM) accounts to database accounts is enabled"
default = false
}
-
+
variable "timeouts" {
type = object({
create = string
@@ -271,10 +271,10 @@ variable "timeouts" {
}
variable "tagname" {
- type = string
- default = "Qb"
+ type = string
+ default = "Qb"
description = "Common name prefix"
-
+
}
variable "license_model" {
@@ -296,14 +296,14 @@ variable "associate_security_group_ids" {
}
variable "enabled" {
- type = bool
- default = true
-
+ type = bool
+ default = true
+
}
-variable "db-identifier"{
+variable "db-identifier" {
type = string
description = "Database identifer"
- default = "qb-db"
+ default = "qb-db"
}