feat(P56b): author 11 L1 module terraform subdirs + fix adapter output format
EXECUTE stage. Authors the remaining 11 L1 module terraform subdirs with the full versions/variables/locals/main/outputs split. Defaults previously hardcoded in the adapter move into locals.tf. Simple single-resource modules (7): - kms-key: aws_kms_key + alias (enable_key_rotation, deletion_window defaults) - ecr: aws_ecr_repository (encryption_configuration from kms_key_arn, image_scanning) - ecs-cluster: aws_ecs_cluster (name default) - iam-role: aws_iam_role + inline_policy (assume_role_policy fallback, ECR/logs policy in locals.tf) - rds: aws_db_instance (storage_encrypted, multi_az, kms_key_arn defaults) - waf: aws_wafv2_web_acl (default_action, visibility_config, dynamic rules) - uptime: aws_ecs_task_definition + aws_ecs_service (Fargate compat, container_definitions in locals.tf) Multi-resource modules with intra-refs (4): - vpc: aws_vpc + aws_subnet + aws_internet_gateway + aws_route_table (CIDR derivation in locals.tf) - ecs-service: aws_ecs_task_definition + aws_ecs_service (Fargate compat, container_definitions, network_config in locals.tf) - alb: aws_lb + aws_lb_target_group + aws_lb_listener (subnet/security_group list derivation in locals.tf) - cloudfront: aws_cloudfront_distribution + aws_cloudfront_origin_access_control (OAC defaults in locals.tf) Registry: terraform_dir added to all 11 remaining entries. Adapter fix: stack output format uses separate 'from' + 'output' fields (not 'from': 'rid.output'). Fixed _emit_root_output to read both fields. 6 previously-skipped tests unblocked (run_platform.sh --check-only now resolves static-assets.yml through the new module-assembled adapter). Removed skip markers. Fixed test assertion (aws_s3_bucket → module). Regression: 461 passed, 0 skipped, 5 deselected (slow). All 12 modules pass run_primitive_plan.sh --check-only. All 12 terraform/ subdirs pass terraform init + validate standalone. ---ci--- project: acdl phase: P56b milestone: v1.11 status: execute ---/ci---
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
locals {
|
||||
subnet_list = split(",", var.subnets)
|
||||
security_groups = var.security_group != null ? [var.security_group] : []
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
resource "aws_lb" "this" {
|
||||
name = var.name
|
||||
load_balancer_type = var.load_balancer_type
|
||||
subnets = local.subnet_list
|
||||
security_groups = local.security_groups
|
||||
}
|
||||
|
||||
resource "aws_lb_target_group" "this" {
|
||||
name = var.name
|
||||
port = var.port
|
||||
protocol = var.protocol
|
||||
vpc_id = var.vpc_id
|
||||
target_type = var.target_type
|
||||
}
|
||||
|
||||
resource "aws_lb_listener" "this" {
|
||||
load_balancer_arn = aws_lb.this.id
|
||||
port = var.port
|
||||
protocol = var.protocol
|
||||
|
||||
default_action {
|
||||
type = "forward"
|
||||
target_group_arn = aws_lb_target_group.this.arn
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
output "lb_arn" {
|
||||
value = aws_lb.this.id
|
||||
description = "The load balancer ARN."
|
||||
}
|
||||
|
||||
output "listener_arn" {
|
||||
value = aws_lb_listener.this.arn
|
||||
description = "The listener ARN."
|
||||
}
|
||||
|
||||
output "target_group_arn" {
|
||||
value = aws_lb_target_group.this.arn
|
||||
description = "The target group ARN."
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
variable "name" {
|
||||
type = string
|
||||
description = "Name tag for the load balancer and child resources."
|
||||
default = "app"
|
||||
}
|
||||
|
||||
variable "subnets" {
|
||||
type = string
|
||||
description = "Comma-separated subnet ids (ref to vpc)."
|
||||
}
|
||||
|
||||
variable "security_group" {
|
||||
type = string
|
||||
description = "Security group id for the load balancer."
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "port" {
|
||||
type = number
|
||||
description = "Listener port (default 80)."
|
||||
default = 80
|
||||
}
|
||||
|
||||
variable "protocol" {
|
||||
type = string
|
||||
description = "Listener protocol (default HTTP)."
|
||||
default = "HTTP"
|
||||
}
|
||||
|
||||
variable "region" {
|
||||
type = string
|
||||
description = "AWS region (provider-level; not a resource arg)."
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "load_balancer_type" {
|
||||
type = string
|
||||
description = "Load balancer type (application or network)."
|
||||
default = "application"
|
||||
}
|
||||
|
||||
variable "target_type" {
|
||||
type = string
|
||||
description = "Target group target type (ip or instance)."
|
||||
default = "ip"
|
||||
}
|
||||
|
||||
variable "vpc_id" {
|
||||
type = string
|
||||
description = "VPC ID for the target group (ref to vpc or platform VPC)."
|
||||
default = null
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
terraform {
|
||||
required_version = ">= 1.9, < 1.10"
|
||||
required_providers {
|
||||
aws = {
|
||||
source = "hashicorp/aws"
|
||||
version = "~> 5.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
locals {
|
||||
# OAC defaults (adapter previously hardcoded these).
|
||||
oac_name = "acdl-oac"
|
||||
oac_origin_type = "s3"
|
||||
oac_signing_behavior = "always"
|
||||
oac_signing_protocol = "sigv4"
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
resource "aws_cloudfront_origin_access_control" "this" {
|
||||
name = local.oac_name
|
||||
origin_access_control_origin_type = local.oac_origin_type
|
||||
signing_behavior = local.oac_signing_behavior
|
||||
signing_protocol = local.oac_signing_protocol
|
||||
}
|
||||
|
||||
resource "aws_cloudfront_distribution" "this" {
|
||||
origin {
|
||||
origin_id = "s3-origin"
|
||||
domain_name = var.bucket_regional_domain_name
|
||||
origin_access_control_id = aws_cloudfront_origin_access_control.this.id
|
||||
s3_origin_config {
|
||||
origin_access_identity = ""
|
||||
}
|
||||
}
|
||||
|
||||
enabled = true
|
||||
price_class = var.price_class
|
||||
default_cache_behavior {
|
||||
viewer_protocol_policy = var.viewer_protocol_policy
|
||||
target_origin_id = "s3-origin"
|
||||
min_ttl = 0
|
||||
default_ttl = var.default_ttl
|
||||
max_ttl = var.max_ttl
|
||||
allowed_methods = ["GET", "HEAD"]
|
||||
cached_methods = ["GET", "HEAD"]
|
||||
forwarded_values {
|
||||
query_string = false
|
||||
cookies {
|
||||
forward = "none"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
restrictions {
|
||||
geo_restriction {
|
||||
restriction_type = "none"
|
||||
}
|
||||
}
|
||||
|
||||
viewer_certificate {
|
||||
cloudfront_default_certificate = true
|
||||
}
|
||||
|
||||
web_acl_id = var.waf_web_acl_arn
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
output "distribution_arn" {
|
||||
value = aws_cloudfront_distribution.this.arn
|
||||
description = "The CloudFront distribution ARN."
|
||||
}
|
||||
|
||||
output "distribution_domain_name" {
|
||||
value = aws_cloudfront_distribution.this.domain_name
|
||||
description = "The CloudFront distribution domain name."
|
||||
}
|
||||
|
||||
output "oac_id" {
|
||||
value = aws_cloudfront_origin_access_control.this.id
|
||||
description = "The Origin Access Control ID."
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
variable "bucket_regional_domain_name" {
|
||||
type = string
|
||||
description = "The S3 bucket regional domain name (ref to s3 origin)."
|
||||
}
|
||||
|
||||
variable "price_class" {
|
||||
type = string
|
||||
description = "CloudFront price class (default PriceClass_100)."
|
||||
default = "PriceClass_100"
|
||||
}
|
||||
|
||||
variable "viewer_protocol_policy" {
|
||||
type = string
|
||||
description = "Viewer protocol policy (default redirect-to-https)."
|
||||
default = "redirect-to-https"
|
||||
}
|
||||
|
||||
variable "default_ttl" {
|
||||
type = number
|
||||
description = "Default TTL in seconds (default 3600)."
|
||||
default = 3600
|
||||
}
|
||||
|
||||
variable "max_ttl" {
|
||||
type = number
|
||||
description = "Max TTL in seconds (default 86400)."
|
||||
default = 86400
|
||||
}
|
||||
|
||||
variable "waf_web_acl_arn" {
|
||||
type = string
|
||||
description = "WAF web ACL ARN to associate (optional, ref to waf)."
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "region" {
|
||||
type = string
|
||||
description = "AWS region (CloudFront is global but the provider region is used for the OAC)."
|
||||
default = null
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
terraform {
|
||||
required_version = ">= 1.9, < 1.10"
|
||||
required_providers {
|
||||
aws = {
|
||||
source = "hashicorp/aws"
|
||||
version = "~> 5.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
locals {
|
||||
encryption_config = var.kms_key_arn != null ? {
|
||||
encryption_type = "KMS"
|
||||
kms_key = var.kms_key_arn
|
||||
} : null
|
||||
}
|
||||
|
||||
resource "aws_ecr_repository" "this" {
|
||||
name = var.name
|
||||
image_tag_mutability = "MUTABLE"
|
||||
image_scanning_configuration {
|
||||
scan_on_push = true
|
||||
}
|
||||
dynamic "encryption_configuration" {
|
||||
for_each = local.encryption_config != null ? [local.encryption_config] : []
|
||||
content {
|
||||
encryption_type = encryption_configuration.value.encryption_type
|
||||
kms_key = encryption_configuration.value.kms_key
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
output "repository_url" {
|
||||
value = aws_ecr_repository.this.repository_url
|
||||
description = "The ECR repository URL."
|
||||
}
|
||||
|
||||
output "repository_arn" {
|
||||
value = aws_ecr_repository.this.arn
|
||||
description = "The ECR repository ARN."
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
variable "name" {
|
||||
type = string
|
||||
description = "ECR repository name."
|
||||
}
|
||||
|
||||
variable "region" {
|
||||
type = string
|
||||
description = "AWS region (provider-level; not a resource arg)."
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "kms_key_arn" {
|
||||
type = string
|
||||
description = "ARN of the CMK for ECR encryption; if absent, uses managed key."
|
||||
default = null
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
terraform {
|
||||
required_version = ">= 1.9, < 1.10"
|
||||
required_providers {
|
||||
aws = {
|
||||
source = "hashicorp/aws"
|
||||
version = "~> 5.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
resource "aws_ecs_cluster" "this" {
|
||||
name = var.name
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
output "cluster_arn" {
|
||||
value = aws_ecs_cluster.this.arn
|
||||
description = "The ECS cluster ARN."
|
||||
}
|
||||
|
||||
output "cluster_id" {
|
||||
value = aws_ecs_cluster.this.id
|
||||
description = "The ECS cluster ID."
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
variable "name" {
|
||||
type = string
|
||||
description = "ECS cluster name."
|
||||
default = "acdl-cluster"
|
||||
}
|
||||
|
||||
variable "region" {
|
||||
type = string
|
||||
description = "AWS region (provider-level; not a resource arg)."
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "kms_key_arn" {
|
||||
type = string
|
||||
description = "ARN of the CMK for CloudWatch log group encryption; if absent, uses managed key."
|
||||
default = null
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
terraform {
|
||||
required_version = ">= 1.9, < 1.10"
|
||||
required_providers {
|
||||
aws = {
|
||||
source = "hashicorp/aws"
|
||||
version = "~> 5.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
locals {
|
||||
# Fargate compat defaults (adapter previously hardcoded these).
|
||||
requires_compatibilities = var.launch_type == "FARGATE" ? ["FARGATE"] : ["EC2"]
|
||||
network_mode = var.launch_type == "FARGATE" ? "awsvpc" : "bridge"
|
||||
|
||||
# Container definitions from image/port/env (adapter previously hardcoded this).
|
||||
container_definitions = jsonencode([{
|
||||
name = "app"
|
||||
image = var.image
|
||||
essential = true
|
||||
portMappings = [{
|
||||
containerPort = var.port
|
||||
hostPort = var.port
|
||||
protocol = "tcp"
|
||||
}]
|
||||
}])
|
||||
|
||||
# Subnet list from comma-separated string.
|
||||
subnet_list = split(",", var.subnets)
|
||||
|
||||
# Security groups list.
|
||||
security_groups = var.security_group != null ? [var.security_group] : []
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
resource "aws_ecs_task_definition" "this" {
|
||||
family = var.family
|
||||
cpu = tostring(var.cpu)
|
||||
memory = tostring(var.memory)
|
||||
requires_compatibilities = local.requires_compatibilities
|
||||
network_mode = local.network_mode
|
||||
container_definitions = local.container_definitions
|
||||
}
|
||||
|
||||
resource "aws_ecs_service" "this" {
|
||||
name = "acdl-microservice"
|
||||
cluster = var.cluster_arn
|
||||
task_definition = aws_ecs_task_definition.this.arn
|
||||
desired_count = var.desired_count
|
||||
launch_type = var.launch_type
|
||||
|
||||
network_configuration {
|
||||
subnets = local.subnet_list
|
||||
security_groups = local.security_groups
|
||||
assign_public_ip = var.launch_type == "FARGATE"
|
||||
}
|
||||
|
||||
dynamic "load_balancer" {
|
||||
for_each = var.lb_target_group_arn != null ? [1] : []
|
||||
content {
|
||||
target_group_arn = var.lb_target_group_arn
|
||||
container_name = "app"
|
||||
container_port = var.port
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
output "service_arn" {
|
||||
value = aws_ecs_service.this.id
|
||||
description = "The ECS service ARN."
|
||||
}
|
||||
|
||||
output "task_def_arn" {
|
||||
value = aws_ecs_task_definition.this.arn
|
||||
description = "The ECS task definition ARN."
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
variable "image" {
|
||||
type = string
|
||||
description = "ECR image URL for the task container."
|
||||
}
|
||||
|
||||
variable "port" {
|
||||
type = number
|
||||
description = "Container port the service listens on."
|
||||
default = 80
|
||||
}
|
||||
|
||||
variable "cpu" {
|
||||
type = number
|
||||
description = "Task CPU units (Fargate)."
|
||||
default = 256
|
||||
}
|
||||
|
||||
variable "memory" {
|
||||
type = number
|
||||
description = "Task memory (MiB, Fargate)."
|
||||
default = 512
|
||||
}
|
||||
|
||||
variable "env" {
|
||||
type = string
|
||||
description = "Environment variables as a JSON map string (optional)."
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "cluster_arn" {
|
||||
type = string
|
||||
description = "ECS cluster ARN (ref to ecs-cluster)."
|
||||
}
|
||||
|
||||
variable "subnets" {
|
||||
type = string
|
||||
description = "Comma-separated subnet ids (ref to vpc)."
|
||||
}
|
||||
|
||||
variable "security_group" {
|
||||
type = string
|
||||
description = "Security group id for the service ENIs."
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "lb_target_group_arn" {
|
||||
type = string
|
||||
description = "Optional ALB target group ARN (ref to alb)."
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "region" {
|
||||
type = string
|
||||
description = "AWS region (provider-level; not a resource arg)."
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "kms_key_arn" {
|
||||
type = string
|
||||
description = "ARN of the CMK for CloudWatch log group encryption; if absent, uses managed key."
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "desired_count" {
|
||||
type = number
|
||||
description = "Desired number of ECS task replicas (Fargate)."
|
||||
default = 1
|
||||
}
|
||||
|
||||
variable "launch_type" {
|
||||
type = string
|
||||
description = "ECS launch type (FARGATE or EC2)."
|
||||
default = "FARGATE"
|
||||
}
|
||||
|
||||
variable "family" {
|
||||
type = string
|
||||
description = "ECS task definition family name."
|
||||
default = "app"
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
terraform {
|
||||
required_version = ">= 1.9, < 1.10"
|
||||
required_providers {
|
||||
aws = {
|
||||
source = "hashicorp/aws"
|
||||
version = "~> 5.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
locals {
|
||||
# Default ECS task trust policy when none is supplied (adapter previously hardcoded this).
|
||||
assume_role_policy = var.assume_role_policy != null ? var.assume_role_policy : jsonencode({
|
||||
Version = "2012-10-17"
|
||||
Statement = [{
|
||||
Effect = "Allow"
|
||||
Principal = { Service = "ecs-tasks.amazonaws.com" }
|
||||
Action = "sts:AssumeRole"
|
||||
}]
|
||||
})
|
||||
|
||||
# Default inline ECR+logs policy when no managed_policies supplied (adapter previously hardcoded this).
|
||||
managed_policy_arns = var.managed_policies != null ? split(",", var.managed_policies) : []
|
||||
|
||||
inline_policy = var.managed_policies == null ? {
|
||||
name = "ecr-logs"
|
||||
policy = jsonencode({
|
||||
Version = "2012-10-17"
|
||||
Statement = [
|
||||
{
|
||||
Effect = "Allow"
|
||||
Action = [
|
||||
"ecr:GetDownloadUrlForLayer",
|
||||
"ecr:BatchGetImage",
|
||||
"ecr:GetAuthorizationToken",
|
||||
"ecr:BatchCheckLayerAvailability"
|
||||
]
|
||||
Resource = "*"
|
||||
},
|
||||
{
|
||||
Effect = "Allow"
|
||||
Action = [
|
||||
"logs:CreateLogGroup",
|
||||
"logs:CreateLogStream",
|
||||
"logs:PutLogEvents"
|
||||
]
|
||||
Resource = "*"
|
||||
}
|
||||
]
|
||||
})
|
||||
} : null
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
resource "aws_iam_role" "this" {
|
||||
name = var.role_name
|
||||
assume_role_policy = local.assume_role_policy
|
||||
}
|
||||
|
||||
resource "aws_iam_role_policy" "ecr_logs" {
|
||||
count = local.inline_policy != null ? 1 : 0
|
||||
name = local.inline_policy.name
|
||||
role = aws_iam_role.this.id
|
||||
policy = local.inline_policy.policy
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
output "role_arn" {
|
||||
value = aws_iam_role.this.arn
|
||||
description = "The IAM role ARN."
|
||||
}
|
||||
|
||||
output "role_id" {
|
||||
value = aws_iam_role.this.id
|
||||
description = "The IAM role ID."
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
variable "role_name" {
|
||||
type = string
|
||||
description = "The IAM role name."
|
||||
default = "acdl-microservice-role"
|
||||
}
|
||||
|
||||
variable "assume_role_policy" {
|
||||
type = string
|
||||
description = "Assume-role policy document (JSON string)."
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "managed_policies" {
|
||||
type = string
|
||||
description = "Comma-separated list of managed policy ARNs to attach."
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "region" {
|
||||
type = string
|
||||
description = "AWS region (provider-level; not a resource arg)."
|
||||
default = null
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
terraform {
|
||||
required_version = ">= 1.9, < 1.10"
|
||||
required_providers {
|
||||
aws = {
|
||||
source = "hashicorp/aws"
|
||||
version = "~> 5.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
resource "aws_kms_key" "this" {
|
||||
description = var.description
|
||||
enable_key_rotation = true
|
||||
deletion_window_in_days = var.deletion_window_days
|
||||
}
|
||||
|
||||
resource "aws_kms_alias" "this" {
|
||||
name = "alias/acdl-${var.description != null ? var.description : "default"}"
|
||||
target_key_id = aws_kms_key.this.key_id
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
output "kms_key_arn" {
|
||||
value = aws_kms_key.this.arn
|
||||
description = "The KMS key ARN."
|
||||
}
|
||||
|
||||
output "kms_key_id" {
|
||||
value = aws_kms_key.this.key_id
|
||||
description = "The KMS key ID."
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
variable "description" {
|
||||
type = string
|
||||
description = "Description of the KMS key."
|
||||
default = "ACDL per-stack CMK"
|
||||
}
|
||||
|
||||
variable "region" {
|
||||
type = string
|
||||
description = "AWS region (provider-level; not a resource arg)."
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "deletion_window_days" {
|
||||
type = number
|
||||
description = "Deletion window in days (7-30)."
|
||||
default = 30
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
terraform {
|
||||
required_version = ">= 1.9, < 1.10"
|
||||
required_providers {
|
||||
aws = {
|
||||
source = "hashicorp/aws"
|
||||
version = "~> 5.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
resource "aws_db_instance" "this" {
|
||||
engine = var.engine
|
||||
engine_version = var.engine_version
|
||||
instance_class = var.instance_class
|
||||
allocated_storage = var.allocated_storage
|
||||
db_name = var.db_name
|
||||
username = var.username
|
||||
multi_az = var.multi_az
|
||||
storage_encrypted = var.storage_encrypted
|
||||
kms_key_id = var.kms_key_arn
|
||||
skip_final_snapshot = true
|
||||
publicly_accessible = false
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
output "db_endpoint" {
|
||||
value = aws_db_instance.this.endpoint
|
||||
description = "The RDS instance endpoint."
|
||||
}
|
||||
|
||||
output "db_arn" {
|
||||
value = aws_db_instance.this.arn
|
||||
description = "The RDS instance ARN."
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
variable "engine" {
|
||||
type = string
|
||||
description = "Database engine (postgres, mysql, etc.)."
|
||||
}
|
||||
|
||||
variable "engine_version" {
|
||||
type = string
|
||||
description = "Database engine version."
|
||||
}
|
||||
|
||||
variable "instance_class" {
|
||||
type = string
|
||||
description = "RDS instance class (e.g. db.t3.micro)."
|
||||
}
|
||||
|
||||
variable "allocated_storage" {
|
||||
type = number
|
||||
description = "Allocated storage in GB."
|
||||
default = 20
|
||||
}
|
||||
|
||||
variable "db_name" {
|
||||
type = string
|
||||
description = "Database name."
|
||||
}
|
||||
|
||||
variable "username" {
|
||||
type = string
|
||||
description = "Database admin username."
|
||||
}
|
||||
|
||||
variable "multi_az" {
|
||||
type = bool
|
||||
description = "Enable Multi-AZ."
|
||||
default = false
|
||||
}
|
||||
|
||||
variable "storage_encrypted" {
|
||||
type = bool
|
||||
description = "Enable storage encryption."
|
||||
default = true
|
||||
}
|
||||
|
||||
variable "region" {
|
||||
type = string
|
||||
description = "AWS region (provider-level; not a resource arg)."
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "kms_key_arn" {
|
||||
type = string
|
||||
description = "ARN of the CMK for storage encryption; if absent, uses managed key."
|
||||
default = null
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
terraform {
|
||||
required_version = ">= 1.9, < 1.10"
|
||||
required_providers {
|
||||
aws = {
|
||||
source = "hashicorp/aws"
|
||||
version = "~> 5.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
locals {
|
||||
container_definitions = jsonencode([{
|
||||
name = "uptime-kuma"
|
||||
image = var.container_image
|
||||
essential = true
|
||||
portMappings = [{
|
||||
containerPort = 3001
|
||||
hostPort = 3001
|
||||
protocol = "tcp"
|
||||
}]
|
||||
}])
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
resource "aws_ecs_task_definition" "uptime" {
|
||||
family = "acdl-uptime"
|
||||
cpu = tostring(var.cpu)
|
||||
memory = tostring(var.memory)
|
||||
requires_compatibilities = ["FARGATE"]
|
||||
network_mode = "awsvpc"
|
||||
container_definitions = local.container_definitions
|
||||
}
|
||||
|
||||
resource "aws_ecs_service" "uptime" {
|
||||
name = "acdl-uptime"
|
||||
cluster = "default"
|
||||
task_definition = aws_ecs_task_definition.uptime.arn
|
||||
desired_count = var.feature_flag_enabled ? 1 : 0
|
||||
launch_type = "FARGATE"
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
output "uptime_url" {
|
||||
value = var.uptime_url != null ? var.uptime_url : "http://localhost:3001"
|
||||
description = "The uptime-kuma URL."
|
||||
}
|
||||
|
||||
output "service_arn" {
|
||||
value = aws_ecs_service.uptime.id
|
||||
description = "The uptime ECS service ARN."
|
||||
}
|
||||
|
||||
output "task_definition_arn" {
|
||||
value = aws_ecs_task_definition.uptime.arn
|
||||
description = "The uptime task definition ARN."
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
variable "container_image" {
|
||||
type = string
|
||||
description = "Container image for the uptime-kuma service."
|
||||
}
|
||||
|
||||
variable "region" {
|
||||
type = string
|
||||
description = "AWS region (provider-level; not a resource arg)."
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "uptime_url" {
|
||||
type = string
|
||||
description = "The uptime-kuma URL (output)."
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "monitored_endpoints" {
|
||||
type = any
|
||||
description = "List of monitored endpoint objects."
|
||||
default = []
|
||||
}
|
||||
|
||||
variable "static_checks" {
|
||||
type = any
|
||||
description = "Static check configuration."
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "alert_channels" {
|
||||
type = any
|
||||
description = "Alert channel configuration."
|
||||
default = {}
|
||||
}
|
||||
|
||||
variable "feature_flag_enabled" {
|
||||
type = bool
|
||||
description = "Feature flag to enable/disable the uptime service."
|
||||
default = true
|
||||
}
|
||||
|
||||
variable "cpu" {
|
||||
type = number
|
||||
description = "Task CPU units (Fargate)."
|
||||
default = 256
|
||||
}
|
||||
|
||||
variable "memory" {
|
||||
type = number
|
||||
description = "Task memory (MiB, Fargate)."
|
||||
default = 512
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
terraform {
|
||||
required_version = ">= 1.9, < 1.10"
|
||||
required_providers {
|
||||
aws = {
|
||||
source = "hashicorp/aws"
|
||||
version = "~> 5.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
locals {
|
||||
cidr_block = var.cidr != null ? var.cidr : "10.0.0.0/16"
|
||||
az_list = split(",", var.azs)
|
||||
name_tag = var.name != null ? var.name : "acdl-vpc"
|
||||
|
||||
# Derive subnet CIDRs from the VPC CIDR
|
||||
subnet_cidrs = [
|
||||
for i in range(length(local.az_list)) : cidrsubnet(local.cidr_block, 8, i + 1)
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
resource "aws_vpc" "this" {
|
||||
cidr_block = local.cidr_block
|
||||
tags = {
|
||||
Name = local.name_tag
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_subnet" "this" {
|
||||
count = length(local.az_list)
|
||||
vpc_id = aws_vpc.this.id
|
||||
cidr_block = local.subnet_cidrs[count.index]
|
||||
availability_zone = local.az_list[count.index]
|
||||
tags = {
|
||||
Name = "${local.name_tag}-subnet-${count.index}"
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_internet_gateway" "this" {
|
||||
vpc_id = aws_vpc.this.id
|
||||
tags = {
|
||||
Name = "${local.name_tag}-igw"
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_route_table" "this" {
|
||||
vpc_id = aws_vpc.this.id
|
||||
route {
|
||||
cidr_block = "0.0.0.0/0"
|
||||
gateway_id = aws_internet_gateway.this.id
|
||||
}
|
||||
tags = {
|
||||
Name = "${local.name_tag}-rt"
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_route_table_association" "this" {
|
||||
count = length(local.az_list)
|
||||
subnet_id = aws_subnet.this[count.index].id
|
||||
route_table_id = aws_route_table.this.id
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
output "vpc_id" {
|
||||
value = aws_vpc.this.id
|
||||
description = "The VPC id."
|
||||
}
|
||||
|
||||
output "subnet_ids" {
|
||||
value = join(",", aws_subnet.this[*].id)
|
||||
description = "Comma-separated subnet ids."
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
variable "cidr" {
|
||||
type = string
|
||||
description = "VPC CIDR block, e.g. 10.0.0.0/16."
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "azs" {
|
||||
type = string
|
||||
description = "Comma-separated availability zones, e.g. us-east-1a,us-east-1b."
|
||||
default = "us-east-1a"
|
||||
}
|
||||
|
||||
variable "name" {
|
||||
type = string
|
||||
description = "Name tag for the VPC and child resources."
|
||||
default = "acdl-vpc"
|
||||
}
|
||||
|
||||
variable "region" {
|
||||
type = string
|
||||
description = "AWS region (provider-level; not a resource arg)."
|
||||
default = null
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
terraform {
|
||||
required_version = ">= 1.9, < 1.10"
|
||||
required_providers {
|
||||
aws = {
|
||||
source = "hashicorp/aws"
|
||||
version = "~> 5.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
locals {
|
||||
action_type = var.default_action == "block" ? "block" : "allow"
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
resource "aws_wafv2_web_acl" "this" {
|
||||
name = var.name
|
||||
scope = var.scope
|
||||
|
||||
default_action {
|
||||
dynamic "allow" {
|
||||
for_each = local.action_type == "allow" ? [1] : []
|
||||
content {}
|
||||
}
|
||||
dynamic "block" {
|
||||
for_each = local.action_type == "block" ? [1] : []
|
||||
content {}
|
||||
}
|
||||
}
|
||||
|
||||
visibility_config {
|
||||
cloudwatch_metrics_enabled = true
|
||||
metric_name = "acdl-waf-metrics"
|
||||
sampled_requests_enabled = true
|
||||
}
|
||||
|
||||
dynamic "rule" {
|
||||
for_each = var.rules
|
||||
content {
|
||||
name = lookup(rule.value, "name", "custom-rule-${rule.key}")
|
||||
priority = lookup(rule.value, "priority", rule.key)
|
||||
override_action {
|
||||
none {}
|
||||
}
|
||||
statement {
|
||||
byte_match_statement {
|
||||
search_string = lookup(rule.value, "search_string", "/")
|
||||
positional_constraint = "CONTAINS"
|
||||
field_to_match {
|
||||
single_header {
|
||||
name = "user-agent"
|
||||
}
|
||||
}
|
||||
text_transformation {
|
||||
priority = 0
|
||||
type = "NONE"
|
||||
}
|
||||
}
|
||||
}
|
||||
visibility_config {
|
||||
cloudwatch_metrics_enabled = true
|
||||
metric_name = "${lookup(rule.value, "name", "custom-rule-${rule.key}")}-metrics"
|
||||
sampled_requests_enabled = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
output "web_acl_arn" {
|
||||
value = aws_wafv2_web_acl.this.arn
|
||||
description = "The WAF Web ACL ARN."
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
variable "name" {
|
||||
type = string
|
||||
description = "WAF Web ACL name."
|
||||
default = "acdl-waf"
|
||||
}
|
||||
|
||||
variable "scope" {
|
||||
type = string
|
||||
description = "WAF scope (CLOUDFRONT or REGIONAL)."
|
||||
default = "CLOUDFRONT"
|
||||
}
|
||||
|
||||
variable "default_action" {
|
||||
type = string
|
||||
description = "Default action (allow or block)."
|
||||
default = "allow"
|
||||
}
|
||||
|
||||
variable "rules" {
|
||||
type = any
|
||||
description = "Custom WAF rules (list of rule objects)."
|
||||
default = []
|
||||
}
|
||||
|
||||
variable "region" {
|
||||
type = string
|
||||
description = "AWS region (provider-level; not a resource arg)."
|
||||
default = null
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
terraform {
|
||||
required_version = ">= 1.9, < 1.10"
|
||||
required_providers {
|
||||
aws = {
|
||||
source = "hashicorp/aws"
|
||||
version = "~> 5.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user