diff --git a/.github/workflows/infra.yml b/.github/workflows/infra.yml index 0e6944f0..ef27fb0e 100644 --- a/.github/workflows/infra.yml +++ b/.github/workflows/infra.yml @@ -2,26 +2,25 @@ name: 'Infra' on: pull_request: - push: - branches: - - main + env: PLATFORM_REPO: "cldcvr/terrarium" - PLATFORM_BRANCH: "shwetanshu/TER-89" + PLATFORM_BRANCH: "main" PLATFORM_DIR: "examples/platform-demo/platform" - TERRARIUM_VERSION: "v0.2" + TERRARIUM_VERSION: "v0.6" TF_API_TOKEN: "${{ secrets.TF_API_TOKEN }}" TF_CLOUD_ORGANIZATION: "kanak" TF_WORKSPACE: "demo-go-pgsql-workflow" TF_CONFIG_DIRECTORY: "./.terrarium-output" + jobs: t8-generate: name: "Terrarium generate" runs-on: ubuntu-latest permissions: - contents: read + contents: write pull-requests: write steps: - name: Checkout @@ -44,119 +43,13 @@ jobs: - name: Terrarium Generate run: | + rm -rf $TF_CONFIG_DIRECTORY terrarium generate -p .platform-repo/$PLATFORM_DIR -a . -o $TF_CONFIG_DIRECTORY/src - cp -r .platform-repo/examples/platform-demo/modules ./$TF_CONFIG_DIRECTORY/modules - - - name: Archive Terrarium generated code - uses: actions/upload-artifact@v3 - with: - name: terrarium-generate-code - path: ${{ env.TF_CONFIG_DIRECTORY }} - - tf-plan: - if: github.ref != 'refs/heads/main' - needs: t8-generate - name: "Terraform plan" - runs-on: ubuntu-latest - permissions: - contents: read - pull-requests: write - steps: - - name: Download Terrarium generated code - uses: actions/download-artifact@v3 - with: - name: terrarium-generate-code - path: ${{ env.TF_CONFIG_DIRECTORY }} - - - name: Upload Configuration - uses: hashicorp/tfc-workflows-github/actions/upload-configuration@v1.0.0 - id: plan-upload - with: - workspace: ${{ env.TF_WORKSPACE }} - directory: ${{ env.TF_CONFIG_DIRECTORY }} - speculative: true - - - name: Create Plan Run - uses: hashicorp/tfc-workflows-github/actions/create-run@v1.0.0 - id: plan-run - with: - workspace: ${{ env.TF_WORKSPACE }} - configuration_version: ${{ steps.plan-upload.outputs.configuration_version_id }} - plan_only: true - - - name: Get Plan Output - uses: hashicorp/tfc-workflows-github/actions/plan-output@v1.0.0 - id: plan-output - with: - plan: ${{ fromJSON(steps.plan-run.outputs.payload).data.relationships.plan.data.id }} - - - name: Update PR - uses: actions/github-script@v6 - id: plan-comment - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - script: | - // Retrieve existing bot comments for the PR - const { data: comments } = await github.rest.issues.listComments({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: context.issue.number, - }); - const botComment = comments.find(comment => { - return comment.user.type === 'Bot' && comment.body.includes('Terraform Plan Output') - }); - const output = `#### Terraform Plan Output - Plan: ${{ steps.plan-output.outputs.add }} to add, ${{ steps.plan-output.outputs.change }} to change, ${{ steps.plan-output.outputs.destroy }} to destroy. - - [Terraform Plan](${{ steps.plan-run.outputs.run_link }})`; - // Delete previous comment so PR timeline makes sense - if (botComment) { - github.rest.issues.deleteComment({ - owner: context.repo.owner, - repo: context.repo.repo, - comment_id: botComment.id, - }); - } - github.rest.issues.createComment({ - issue_number: context.issue.number, - owner: context.repo.owner, - repo: context.repo.repo, - body: output - }); - - tf-apply: - if: github.ref == 'refs/heads/main' - needs: t8-generate - name: "Terraform apply" - runs-on: ubuntu-latest - permissions: - contents: read - pull-requests: write - steps: - - name: Download Terrarium generated code - uses: actions/download-artifact@v3 - with: - name: terrarium-generate-code - path: .terrarium-output - - - name: Upload Configuration - uses: hashicorp/tfc-workflows-github/actions/upload-configuration@v1.0.0 - id: apply-upload - with: - workspace: ${{ env.TF_WORKSPACE }} - directory: ${{ env.TF_CONFIG_DIRECTORY }} - - - name: Create Apply Run - uses: hashicorp/tfc-workflows-github/actions/create-run@v1.0.0 - id: apply-run - with: - workspace: ${{ env.TF_WORKSPACE }} - configuration_version: ${{ steps.apply-upload.outputs.configuration_version_id }} + cp -r .platform-repo/examples/platform-demo/modules $TF_CONFIG_DIRECTORY/modules + rm -rf .platform-repo - - name: Apply - uses: hashicorp/tfc-workflows-github/actions/apply-run@v1.0.0 - if: fromJSON(steps.apply-run.outputs.payload).data.attributes.actions.IsConfirmable - id: apply + - name: "Push Terrarium generated code at path : ${{ env.TF_CONFIG_DIRECTORY }}" + uses: stefanzweifel/git-auto-commit-action@v4 with: - run: ${{ steps.apply-run.outputs.run_id }} - comment: "Apply Run from GitHub Actions CI ${{ github.sha }}" + commit_message: terrarium-generate-code + branch: ${{ github.head_ref || github.ref_name }} diff --git a/.terrarium-output/modules/alb/alb_main.tf b/.terrarium-output/modules/alb/alb_main.tf new file mode 100644 index 00000000..ad57d44b --- /dev/null +++ b/.terrarium-output/modules/alb/alb_main.tf @@ -0,0 +1,109 @@ +module "alb" { + source = "terraform-aws-modules/alb/aws" + version = "~> 8.7" + + for_each = var.tf_component_lb + + name = "${var.extract_resource_name}-alb" + + create_lb = local.tr_web_service == true ? true : false + + load_balancer_type = each.value.load_balancer_type + create_security_group = true + + vpc_id = var.vpc_id + subnets = var.public_subnet_ids + security_groups = var.security_group_ids + + access_logs = { + bucket = module.s3_bucket[each.key].s3_bucket_id + } + + security_group_rules = [ + { + type = "ingress" + from_port = 80 + to_port = 80 + protocol = "TCP" + cidr_blocks = ["0.0.0.0/0"] + }, + { + type = "ingress" + from_port = 443 + to_port = 443 + protocol = "TCP" + cidr_blocks = ["0.0.0.0/0"] + }, + { + type = "egress" + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = ["0.0.0.0/0"] + } + ] + + + target_groups = [ + for service_key, service_value in var.tr_component_ecs_services : { + name_prefix = substr("${service_key}",0,6) + backend_protocol = "HTTP" + backend_port = try(service_value.port, null) + target_type = "ip" + } + if try(service_value.port, null) != null + ] + + https_listeners = [ + { + port = 443 + protocol = "HTTPS" + certificate_arn = each.value.certificate_arn + target_group_index = 0 + } + ] + + http_tcp_listeners = [ + { + port = 80 + protocol = "HTTP" + action_type = "redirect" + redirect = { + port = "443" + protocol = "HTTPS" + status_code = "HTTP_301" + } + } + ] + + tags = { + Environment = "Test" + } +} + +resource "random_id" "bucket_suffix" { + byte_length = 4 + keepers = { + bucket_base_name = var.extract_resource_name + } +} + +module "s3_bucket" { + source = "terraform-aws-modules/s3-bucket/aws" + version = "3.14.0" + + for_each = var.tf_component_lb + + bucket = "${var.extract_resource_name}-alb-logs-${random_id.bucket_suffix.hex}" + acl = "log-delivery-write" + + # Allow deletion of non-empty bucket + force_destroy = var.environment == "production" || var.environment == "prod" ? false : true + + control_object_ownership = true + object_ownership = "ObjectWriter" + + attach_elb_log_delivery_policy = true # Required for ALB logs + attach_lb_log_delivery_policy = true # Required for ALB/NLB logs +} + diff --git a/.terrarium-output/modules/alb/alb_outputs.tf b/.terrarium-output/modules/alb/alb_outputs.tf new file mode 100644 index 00000000..a9dc7691 --- /dev/null +++ b/.terrarium-output/modules/alb/alb_outputs.tf @@ -0,0 +1,29 @@ +output "alb_names" { + value = [for k, v in module.alb : v.name] + description = "A list of the names of the ALBs that were created." +} + +output "alb_dns_names" { + value = [for k, v in module.alb : v.dns_name] + description = "A list of the DNS names of the ALBs that were created." +} + +output "alb_arns" { + value = [for k, v in module.alb : v.arn] + description = "A list of the ARNs of the ALBs that were created." +} + +output "alb_security_group_ids" { + value = [for k, v in module.alb : v.security_group_id] + description = "A list of the security group IDs of the ALBs that were created." +} + +output "alb_target_group_arns" { + value = module.alb.alb_target_group_arns + description = "A list of the ARNs of the target groups associated with the ALBs." +} + +output "alb_log_bucket_names" { + value = [for k, v in module.s3_bucket : v.bucket] + description = "A list of the names of the S3 buckets used for ALB access logs." +} diff --git a/.terrarium-output/modules/alb/alb_varibles.tf b/.terrarium-output/modules/alb/alb_varibles.tf new file mode 100644 index 00000000..670d745a --- /dev/null +++ b/.terrarium-output/modules/alb/alb_varibles.tf @@ -0,0 +1,34 @@ +variable "extract_resource_name" { + type = string + description = "The base name to use for all resources created by this module." +} + +variable "environment" { + type = string + description = "The environment in which the infrastructure is being deployed (e.g. dev, prod, etc.)." +} + +variable "tf_component_lb" { + type = any + description = "A map of objects that define the load balancers to create." +} + +variable "tr_component_ecs_services" { + type = any + description = "A map of objects that define the ECS services to create." +} + +variable "vpc_id" { + type = string + description = "The ID of the VPC in which to create the load balancer." +} + +variable "public_subnet_ids" { + type = any + description = "A list of IDs of the public subnets in which to create the load balancer." +} + +variable "security_group_ids" { + type = any + description = "A list of IDs of the security groups to associate with the load balancer." +} diff --git a/.terrarium-output/modules/ecs/ecs_main.tf b/.terrarium-output/modules/ecs/ecs_main.tf new file mode 100644 index 00000000..6fcb472c --- /dev/null +++ b/.terrarium-output/modules/ecs/ecs_main.tf @@ -0,0 +1,347 @@ +locals { + tr_component_ecs_combined = { + for cluster, config in var.ecs_config : cluster => { + name = config.name + compute_type = config.engine.type + engine = { + type = config.engine.type + default_weight = config.engine.default_weight + spot_weight = config.engine.spot_weight + } + services = { + for service, details in var.tr_component_ecs_services : service => { + name = try(details.name, null) + cpu = try(details.cpu, 512) + memory = try(details.memory, 1024) + image = try(details.image, null) + port = try(details.port, null) + protocol = try(details.protocol, null) + } + } + } + } +} + + +module "ecs" { + source = "terraform-aws-modules/ecs/aws" + version = "~> 5.2" + + for_each = local.tr_component_ecs_combined + + cluster_name = each.value.name + cluster_configuration = { + execute_command_configuration = { + logging = "OVERRIDE" + log_configuration = { + cloud_watch_log_group_name = "/aws/ecs/aws-ec2" + } + } + } + + fargate_capacity_providers = each.value.compute_type == "FARGATE" ? { + FARGATE = { + default_capacity_provider_strategy = { + weight = each.value.engine.default_weight + } + } + FARGATE_SPOT = { + default_capacity_provider_strategy = { + weight = each.value.engine.spot_weight + } + } + } : null + + services = { + for service_key, service_value in var.tr_component_ecs_services : service_key => { + cpu = service_value.cpu + memory = service_value.memory + environment = try(service_value.environment, null) + create_task_definition = try(service_key, null) != null ? true : false + + container_definitions = { + # Container definition(s) + service_key = { + cpu = service_value.cpu + memory = service_value.memory + essential = true + image = service_value.image + readonly_root_filesystem = false + name = service_value.name + + port_mappings = try(service_value.port, null) != null ? [ + { + name = "${service_key}-port" + containerPort = service_value.port + protocol = service_value.protocol + } + ] : [] + } + } + # To add service discovery + # service_connect_configuration = service_value.port != null ? { + # namespace = var.environment + # service = { + # client_alias = { + # port = service_value.port + # dns_name = "${service_key}-svc" + # } + # port_name = "${service_key}-port" + # discovery_name = "${service_key}-svc" + # } + # } : { } + + service_connect_configuration = { for k, v in { + namespace = var.environment + service = { + client_alias = { + port = try(service_value.port, null) + dns_name = "${service_key}-svc" + } + port_name = "${service_key}-port" + discovery_name = "${service_key}-svc" + } + } : k => v if try(service_value.connect, null) != null } + + + load_balancer = { for k, v in { + service = { + target_group_arn = try(service_value.port, null) != null ? (length([for arn in module.alb["default"].target_group_arns : arn if length(regexall(substr(service_key, 0, 5), arn)) > 0]) > 0 ? ([for arn in module.alb["default"].target_group_arns : arn if length(regexall(substr(service_key, 0, 5), arn)) > 0][0]) : null) : null + container_name = try(service_value.port, null) != null ? service_value.name : null + container_port = try(service_value.port, null) + } + } : k => v if try(service_value.port, null) != null } + + enable_cloudwatch_logging = var.environment == "production" || var.environment == "prod" ? true : false + subnet_ids = var.private_subnet_ids["default"] + create_security_group = try(service_value.port, null) == null ? false : true + security_group_rules = try(service_value.port, null) != null ? { + "alb_ingress_${service_key}" = { + type = "ingress" + from_port = service_value.port + to_port = service_value.port + protocol = service_value.protocol + description = "${service_key}-port" + source_security_group_id = module.sg[service_key].security_group_id + } + egress_all = { + type = "egress" + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = ["0.0.0.0/0"] + } + } : { + "alb_ingress_${service_key}" = { + type = null + from_port = null + to_port = null + protocol = null + description = null + source_security_group_id = null + } + egress_all = { + type = "egress" + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = ["0.0.0.0/0"] + } + } + + } + } + + depends_on = [time_sleep.wait_60_seconds] + + tags = { + Environment = var.environment + Project = "Example" + } +} + +resource "time_sleep" "wait_60_seconds" { + depends_on = [module.alb] + + create_duration = "60s" +} + +resource "aws_lb_listener_rule" "lb-listener-rule" { + for_each = { for k, v in var.tr_component_ecs_services : k => v if lookup(v, "port", null) != null } + listener_arn = module.alb["default"].https_listener_ids[0] + priority = 100 + index(keys(var.tr_component_ecs_services), each.key) + + action { + type = "forward" + target_group_arn = try(each.value.port, null) != null ? (length([for arn in module.alb["default"].target_group_arns : arn if length(regexall(substr(each.key, 0, 5), arn)) > 0]) > 0 ? ([for arn in module.alb["default"].target_group_arns : arn if length(regexall(substr(each.key, 0, 5), arn)) > 0][0]) : null) : null + } + + condition { + path_pattern { + values = [each.value.path] + } + } + condition { + host_header { + values = [each.value.site] + } + } +} + +module "sg" { + source = "terraform-aws-modules/security-group/aws" + version = "5.1.0" + + for_each = var.tr_component_ecs_services + + name = var.extract_resource_name + description = "Security group for ${each.key}-service with custom ports open within VPC" + vpc_id = var.vpc_id["default"] + + ingress_cidr_blocks = [var.vpc_cidr_block["default"]] + egress_with_cidr_blocks = [ + { + from_port = 0 + to_port = 0 + protocol = "-1" + description = "Service name" + cidr_blocks = "0.0.0.0/0" + }, + ] + ingress_with_cidr_blocks = try(each.value.port, []) != [] ? [ + { + from_port = each.value.port + to_port = each.value.port + protocol = each.value.protocol + description = "${each.key}-service ports" + cidr_blocks = var.vpc_cidr_block["default"] + } + ] : [] +} + + +module "alb" { + source = "terraform-aws-modules/alb/aws" + version = "~> 8.0" + + for_each = var.lb_config + + name = "${var.extract_resource_name}-alb" + + create_lb = each.value.create_alb == true ? true : false + + load_balancer_type = each.value.load_balancer_type + create_security_group = true + + vpc_id = var.vpc_id["default"] + subnets = var.public_subnet_ids["default"] + security_groups = [for sg in values(module.sg) : sg.security_group_id] + + access_logs = { + bucket = module.s3_bucket[each.key].s3_bucket_id + } + + security_group_rules = [ + { + type = "ingress" + from_port = 80 + to_port = 80 + protocol = "TCP" + cidr_blocks = ["0.0.0.0/0"] + }, + { + type = "ingress" + from_port = 443 + to_port = 443 + protocol = "TCP" + cidr_blocks = ["0.0.0.0/0"] + }, + { + type = "egress" + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = ["0.0.0.0/0"] + } + ] + + + target_groups = [ + for service_key, service_value in var.tr_component_ecs_services : { + name_prefix = substr("${service_key}", 0, 6) + backend_protocol = "HTTP" + backend_port = try(service_value.port, null) + target_type = "ip" + } + if try(service_value.port, null) != null + ] + + https_listeners = [ + { + port = 443 + protocol = "HTTPS" + certificate_arn = var.certificate_arn + target_group_index = 0 + } + ] + + http_tcp_listeners = [ + { + port = 80 + protocol = "HTTP" + action_type = "redirect" + redirect = { + port = "443" + protocol = "HTTPS" + status_code = "HTTP_301" + } + } + ] + + depends_on = [module.sg] + + tags = { + Environment = "Test" + } +} + +resource "random_id" "bucket_suffix" { + byte_length = 4 + keepers = { + bucket_base_name = var.extract_resource_name + } +} + +module "s3_bucket" { + source = "terraform-aws-modules/s3-bucket/aws" + version = "3.14.0" + + for_each = var.lb_config + + bucket = "${var.extract_resource_name}-alb-logs-${random_id.bucket_suffix.hex}" + acl = "log-delivery-write" + + # Allow deletion of non-empty bucket + force_destroy = var.environment == "production" || var.environment == "prod" ? false : true + + control_object_ownership = true + object_ownership = "ObjectWriter" + + attach_elb_log_delivery_policy = true # Required for ALB logs + attach_lb_log_delivery_policy = true # Required for ALB/NLB logs +} + + +resource "aws_route53_record" "alb_route53_record" { + for_each = module.alb + + zone_id = var.zone_id + name = var.domain_name + type = "A" + + alias { + name = each.value.lb_dns_name + zone_id = each.value.lb_zone_id + evaluate_target_health = false + } +} diff --git a/.terrarium-output/modules/ecs/ecs_outputs.tf b/.terrarium-output/modules/ecs/ecs_outputs.tf new file mode 100644 index 00000000..4478db5a --- /dev/null +++ b/.terrarium-output/modules/ecs/ecs_outputs.tf @@ -0,0 +1,11 @@ +output "ecs_cluster_names" { + value = [for cluster in local.tr_component_ecs_combined : cluster.name] +} + +output "ecs_service_names" { + value = [for service_key, service_value in var.tr_component_ecs_services : service_value.name] +} + +output "alb_outputs" { + value = module.alb +} diff --git a/.terrarium-output/modules/ecs/ecs_variables.tf b/.terrarium-output/modules/ecs/ecs_variables.tf new file mode 100644 index 00000000..02e06f51 --- /dev/null +++ b/.terrarium-output/modules/ecs/ecs_variables.tf @@ -0,0 +1,59 @@ +variable "ecs_config" { + type = any +} + +variable "tr_component_ecs_services" { + type = any +} + +variable "environment" { + type = string + default = "dev" +} + +variable "vpc_id" { + type = any + description = "The ID of the VPC in which to create the load balancer." +} + +variable "public_subnet_ids" { + type = any + description = "A list of IDs of the public subnets in which to create the load balancer." +} + +variable "vpc_cidr_block" { + type = any + description = "The CIDR block of the VPC in which to create the load balancer." +} + +variable "private_subnet_ids" { + type = any + description = "A list of IDs of the private subnets in which to create the ECS Service." +} + +variable "extract_resource_name" { + type = string + description = "The base name to use for all resources created by this module." +} + +variable "lb_config" { + type = any + description = "A map of objects that define the load balancers to create." +} + +variable "certificate_arn" { + type = string + description = "The ARN of the certificate to use for HTTPS listeners." +} + +variable "domain_name" { + description = "The DNS zone domain name to create records in." + type = string + default = "platform.test.codepipes.io" +} + +variable "zone_id" { + description = "The DNS zone ID to create records in." + type = string + default = "placeholder" +} diff --git a/.terrarium-output/modules/postgres/postgres_main.tf b/.terrarium-output/modules/postgres/postgres_main.tf new file mode 100644 index 00000000..322bfe0d --- /dev/null +++ b/.terrarium-output/modules/postgres/postgres_main.tf @@ -0,0 +1,49 @@ +resource "random_password" "password" { + length = 16 + special = true + override_special = "_%@" +} + +module "postgres" { + source = "terraform-aws-modules/rds/aws" + version = "6.0.0" + + for_each = var.postgres_app_config + + identifier = each.key + engine_version = each.value.version + db_name = each.key + engine = try(each.value.engine, "postgres") + allocated_storage = try(each.value.allocated_storage, 20) + username = try(each.value.username, "postgres") + family = try(format("postgres%s", element(split(".", each.value.version), 0)), "postgres13") + password = try(each.value.password, random_password.password.result) + + instance_class = try(each.value.db_instance_class, "db.t2.medium") + db_subnet_group_name = var.postgres_database_subnet_group["default"] + vpc_security_group_ids = [module.postgres_security_group.security_group_id] + subnet_ids = var.postgres_subnet_ids["default"] +} + + +module "postgres_security_group" { + source = "terraform-aws-modules/security-group/aws" + version = "5.1.0" + + name = "postgres_sg-${keys(var.postgres_app_config)[0]}" + vpc_id = var.postgres_vpc_id["default"] + + # ingress + ingress_with_cidr_blocks = [ + { + rule = "postgresql-tcp" + cidr_blocks = var.postgres_cidr_blocks["default"] + } + ] + egress_with_cidr_blocks = [ + { + rule = "all-all" + cidr_blocks = "0.0.0.0/0" + }, + ] +} diff --git a/.terrarium-output/modules/postgres/postgres_outputs.tf b/.terrarium-output/modules/postgres/postgres_outputs.tf new file mode 100644 index 00000000..ac82924b --- /dev/null +++ b/.terrarium-output/modules/postgres/postgres_outputs.tf @@ -0,0 +1,24 @@ +output "postgres_identifiers" { + description = "The identifiers of the created postgres instances" + value = { for key, component in module.postgres : key => component.db_instance_identifier } +} + +output "postgres_endpoints" { + description = "The endpoints of the created postgres instances" + value = { for key, component in module.postgres : key => component.db_instance_endpoint } +} + +output "postgres_security_group_id" { + description = "The ID of the created security group for postgres" + value = module.postgres_security_group.security_group_id +} + +output "db_password" { + description = "The password for the postgres database" + value = { for key, component in module.postgres : key => try(component.password, random_password.password.result) } +} + +output "db_instance_username" { + description = "The username for the postgres database" + value = { for key, component in module.postgres : key => component.db_instance_username } +} diff --git a/.terrarium-output/modules/postgres/postgres_variables.tf b/.terrarium-output/modules/postgres/postgres_variables.tf new file mode 100644 index 00000000..7bd58214 --- /dev/null +++ b/.terrarium-output/modules/postgres/postgres_variables.tf @@ -0,0 +1,24 @@ +variable "postgres_subnet_ids" { + description = "A list of subnet IDs to launch the PostgreSQL instance in" + type = any +} + +variable "postgres_vpc_id" { + description = "The ID of the VPC to launch the PostgreSQL instance in" + type = any +} + +variable "postgres_app_config" { + description = "A map of objects that define the PostgreSQL instance to create" + type = any +} + +variable "postgres_cidr_blocks" { + description = "The CIDR blocks to allow access to the PostgreSQL instance" + type = any +} + +variable "postgres_database_subnet_group" { + description = "A map of objects that define the Postgres database subnet group to create." + type = any +} diff --git a/.terrarium-output/modules/redis/redis_main.tf b/.terrarium-output/modules/redis/redis_main.tf new file mode 100644 index 00000000..1a6d7692 --- /dev/null +++ b/.terrarium-output/modules/redis/redis_main.tf @@ -0,0 +1,48 @@ +module "this" { + source = "cloudposse/label/null" + # Cloud Posse recommends pinning every module to a specific version + # version = "x.x.x" + namespace = var.common_name_prefix + stage = var.environment + name = "${keys(var.tr_component_redis)[0]}-redis" +} + +module "redis" { + source = "cloudposse/elasticache-redis/aws" + # Always pin every module to a specific version + version = "0.52.0" + + for_each = var.tr_component_redis + + description = "Redis cluster for ${keys(var.tr_component_redis)[0]}" + + + availability_zones = length(var.redis_availability_zones["default"]) >= coalesce(var.redis_config.default.number_of_azs, 2) ? slice(var.redis_availability_zones["default"], 0, coalesce(var.redis_config.default.number_of_azs, 2)) : var.redis_availability_zones["default"] + zone_id = can(var.redis_config.default.redis_zone_id) ? var.redis_config.default.redis_zone_id : "" + vpc_id = var.redis_vpc_id["default"] + subnets = length(var.redis_availability_zones["default"]) == 2 ? slice(var.redis_subnet["default"], 0, 2) : [] + + port = coalesce(can(var.redis_config.default.port) ? var.redis_config.default.port : null, 6379) + + cluster_size = var.redis_config.default.cluster_size + instance_type = var.redis_config.default.instance_type + apply_immediately = var.environment == "production" ? false : true + automatic_failover_enabled = var.environment == "production" ? true : false + cluster_mode_enabled = var.environment == "production" ? true : false + engine_version = each.value.version + family = var.redis_config.default.family + create_security_group = true + at_rest_encryption_enabled = var.environment == "production" ? true : var.redis_config.default.at_rest_encryption_enabled + transit_encryption_enabled = var.environment == "production" ? true : var.redis_config.default.transit_encryption_enabled + cluster_mode_replicas_per_node_group = var.environment == "production" ? var.redis_config.default.cluster_mode_replicas_per_node_group : null + cluster_mode_num_node_groups = var.environment == "production" ? var.redis_config.default.cluster_mode_num_node_groups : null + # parameter = [ + # { + # name = "notify-keyspace-events" + # value = "lK" + # } + # ] + + context = module.this.context +} + diff --git a/.terrarium-output/modules/redis/redis_outputs.tf b/.terrarium-output/modules/redis/redis_outputs.tf new file mode 100644 index 00000000..53196584 --- /dev/null +++ b/.terrarium-output/modules/redis/redis_outputs.tf @@ -0,0 +1,51 @@ +output "redis_cluster_address" { + value = { for k, v in module.redis : k => v.host } +} + +output "redis_cluster_endpoint" { + value = { for k, v in module.redis : k => v.endpoint } +} + + +output "redis_cluster_port" { + value = { for k, v in module.redis : k => v.port } +} + +output "redis_cluster_security_group_id" { + value = { for k, v in module.redis : k => v.security_group_id } +} + +output "redis_cluster_id" { + value = { for k, v in module.redis : k => v.id } +} + +output "redis_security_group_name" { + value = { for k, v in module.redis : k => v.security_group_name } + description = "The name of the created security group" +} + +output "redis_cluster_reader_endpoint_address" { + value = { for k, v in module.redis : k => v.reader_endpoint_address } + description = "The address of the endpoint for the reader node in the replication group, if the cluster mode is disabled." +} + +output "redis_member_clusters" { + value = { for k, v in module.redis : k => v.member_clusters } + description = "Redis cluster members" +} + + +output "redis_cluster_arn" { + value = { for k, v in module.redis : k => v.arn } + description = "Elasticache Replication Group ARN" +} + +output "redis_engine_version_actual" { + value = { for k, v in module.redis : k => v.engine_version_actual } + description = "The running version of the cache engine" +} + +output "redis_cluster_enabled" { + value = { for k, v in module.redis : k => v.cluster_enabled } + description = "Indicates if cluster mode is enabled" +} diff --git a/.terrarium-output/modules/redis/redis_variables.tf b/.terrarium-output/modules/redis/redis_variables.tf new file mode 100644 index 00000000..fed29087 --- /dev/null +++ b/.terrarium-output/modules/redis/redis_variables.tf @@ -0,0 +1,40 @@ +variable "common_name_prefix" { + type = string + description = "The common name prefix to use for all resources" +} + +variable "environment" { + type = string + description = "The environment to deploy the resources in" +} + +variable "extract_resource_name" { + type = string + description = "The name of the resource" +} + +variable "tr_component_redis" { + description = "A map of objects that define the Redis cluster to create." + type = any +} + +variable "redis_availability_zones" { + type = any + description = "The availability zones to deploy the Redis cluster in" +} + +variable "redis_vpc_id" { + type = any + description = "The ID of the VPC to deploy the Redis cluster in" +} + + +variable "redis_subnet" { + type = any + description = "The subnets to deploy the Redis cluster in" +} + +variable "redis_config" { + description = "A map of objects that define the Redis database to create." + type = any +} diff --git a/.terrarium-output/modules/vpc/vpc_main.tf b/.terrarium-output/modules/vpc/vpc_main.tf new file mode 100644 index 00000000..f20eae69 --- /dev/null +++ b/.terrarium-output/modules/vpc/vpc_main.tf @@ -0,0 +1,48 @@ + +data "aws_availability_zones" "available" {} + +module "vpc" { + source = "terraform-aws-modules/vpc/aws" + version = "5.0.0" + + for_each = var.tr_component_vpc + + name = "${var.extract_resource_name}-vpc" + cidr = coalesce(each.value.vpc_cidr_block, "10.0.0.0/16") + + manage_default_network_acl = true + manage_default_route_table = true + manage_default_security_group = true + + # Fetch the availability zones based on the number provided in coalesce(each.value.number_of_azs, 2) + azs = slice(data.aws_availability_zones.available.names, 0, coalesce(each.value.number_of_azs, 2)) + + # Generate private subnets based on the VPC CIDR block and the number of availability zones + private_subnets = [for i in range(coalesce(each.value.number_of_azs, 2)) : cidrsubnet(coalesce(each.value.vpc_cidr_block, "10.0.0.0/16"), 8, i)] + + # Generate public subnets based on the VPC CIDR block, the number of availability zones, and the offset of coalesce(each.value.number_of_azs, 2) + public_subnets = [for i in range(coalesce(each.value.number_of_azs, 2)) : cidrsubnet(coalesce(each.value.vpc_cidr_block, "10.0.0.0/16"), 8, i + coalesce(each.value.number_of_azs, 2))] + + database_subnets = [for i in range(coalesce(each.value.number_of_azs, 2)) : cidrsubnet(coalesce(each.value.vpc_cidr_block, "10.0.0.0/16"), 8, 4 + i + coalesce(each.value.number_of_azs, 2))] + + create_database_subnet_group = true + create_database_subnet_route_table = true + + enable_nat_gateway = true + enable_vpn_gateway = true + + # Enable VPC flow logs with role and groups if the environment is production, otherwise disable them + enable_flow_log = var.environment == "production" ? true : false + create_flow_log_cloudwatch_iam_role = var.environment == "production" ? true : false + create_flow_log_cloudwatch_log_group = var.environment == "production" ? true : false + + tags = merge( + { + "Name" = format("%s", "${var.extract_resource_name}-vpc") + }, + { + environment = var.environment + }, + var.tags, + ) +} diff --git a/.terrarium-output/modules/vpc/vpc_output.tf b/.terrarium-output/modules/vpc/vpc_output.tf new file mode 100644 index 00000000..f2ffa8ef --- /dev/null +++ b/.terrarium-output/modules/vpc/vpc_output.tf @@ -0,0 +1,48 @@ +output "vpc_id" { + value = { + for vpc_name, vpc in module.vpc : + vpc_name => vpc.vpc_id + } +} + +output "vpc_cidr_blocks" { + value = { + for vpc_name, vpc in module.vpc : + vpc_name => vpc.vpc_cidr_block + } +} + +output "public_subnet_ids" { + value = { + for vpc_name, vpc in module.vpc : + vpc_name => vpc.public_subnets + } +} + +output "private_subnet_ids" { + value = { + for vpc_name, vpc in module.vpc : + vpc_name => vpc.private_subnets + } +} + +output "availability_zones" { + value = { + for vpc_name, vpc in module.vpc : + vpc_name => vpc.azs + } +} + +output "database_subnets" { + value = { + for vpc_name, vpc in module.vpc : + vpc_name => vpc.database_subnets + } +} + +output "database_subnet_group" { + value = { + for vpc_name, vpc in module.vpc : + vpc_name => vpc.database_subnet_group + } +} diff --git a/.terrarium-output/modules/vpc/vpc_variables.tf b/.terrarium-output/modules/vpc/vpc_variables.tf new file mode 100644 index 00000000..1ba2b760 --- /dev/null +++ b/.terrarium-output/modules/vpc/vpc_variables.tf @@ -0,0 +1,50 @@ +# General variables +variable "environment" { + description = "The name for identifying the type of environment" + type = string +} + +variable "common_name_prefix" { + description = "The prefix used to name all resources created." + type = string +} + +variable "number" { + description = "The count of the resource" + default = 001 +} + +variable "tags" { + type = map(string) + description = "Any tags that should be present on the Virtual Network resources" + default = {} +} + +variable "extract_resource_name" { + type = string + description = "The name of the resource" +} + +#VPC Variables +variable "vpc_cidr_block" { + description = "CIDR Block to be used by VPC" + default = "10.0.0.0/16" + type = string +} + +variable "number_of_azs" { + description = "Number of Availability Zones to use for the VPC" + default = 2 + type = number +} + +variable "vpc_enable_dns_hostnames" { + description = "A boolean flag to enable/disable DNS support in the VPC. Defaults to true." + default = true + type = bool +} + +variable "tr_component_vpc" { + description = "A map of objects that define the VPC to create." + type = any +} diff --git a/.terrarium-output/src/acm.tf b/.terrarium-output/src/acm.tf new file mode 100644 index 00000000..619da670 --- /dev/null +++ b/.terrarium-output/src/acm.tf @@ -0,0 +1,17 @@ +module "acm" { + source = "terraform-aws-modules/acm/aws" + version = "~> 4.0" + + domain_name = var.domain_name + zone_id = var.zone_id + + subject_alternative_names = [ + "*.${var.domain_name}" + ] + + wait_for_validation = true + + tags = { + Name = var.domain_name + } +} diff --git a/.terrarium-output/src/app_banking_app.env.mustache b/.terrarium-output/src/app_banking_app.env.mustache new file mode 100644 index 00000000..fe413de9 --- /dev/null +++ b/.terrarium-output/src/app_banking_app.env.mustache @@ -0,0 +1,2 @@ +BA_LEDGERDB_HOST="{{ tr_component_postgres_host.value.ledgerdb }}" +BA_LEDGERDB_PASSWORD="{{ tr_component_postgres_password.value.ledgerdb }}" diff --git a/.terrarium-output/src/ecs.tf b/.terrarium-output/src/ecs.tf new file mode 100644 index 00000000..15f9ac29 --- /dev/null +++ b/.terrarium-output/src/ecs.tf @@ -0,0 +1,50 @@ +module "ecs" { + source = "../modules/ecs" + + extract_resource_name = local.extract_resource_name + environment = var.environment + + tr_component_ecs_services = merge(local.app_vars_service_web, local.app_vars_service_private) + ecs_config = var.ecs_config + vpc_id = module.vpc.vpc_id + public_subnet_ids = module.vpc.public_subnet_ids + private_subnet_ids = module.vpc.private_subnet_ids + vpc_cidr_block = module.vpc.vpc_cidr_blocks + lb_config = var.lb_config + certificate_arn = module.acm.acm_certificate_arn + domain_name = var.domain_name + zone_id = var.zone_id + + depends_on = [module.acm] + +} + +locals { + app_vars_service_web = { for k, v in local.tr_component_service_web : k => merge(v, + { + public = true + name = k + image = "public.ecr.aws/nginx/nginx:alpine-slim" + site = format("%s.%s", k, var.domain_name) + } + )} + app_vars_service_private = { for k, v in local.tr_component_service_private : k => merge(v, + { + public = false + name = k + image = "public.ecr.aws/nginx/nginx:alpine-slim" + site = format("%s.%s", k, var.domain_name) + } + )} +} + +module "tr_component_service_web" { + source = "cloudposse/label/null" + depends_on = [module.ecs] +} + + + + + + diff --git a/.terrarium-output/src/locals.tf b/.terrarium-output/src/locals.tf new file mode 100644 index 00000000..19a7d2c1 --- /dev/null +++ b/.terrarium-output/src/locals.tf @@ -0,0 +1,4 @@ +locals { + extract_resource_name = "${var.common_name_prefix}-${var.environment}" +} + diff --git a/.terrarium-output/src/outputs.tf b/.terrarium-output/src/outputs.tf new file mode 100644 index 00000000..cb082cf5 --- /dev/null +++ b/.terrarium-output/src/outputs.tf @@ -0,0 +1,48 @@ +output "vpc_id" { + value = module.vpc.vpc_id +} + +output "public_subnets" { + value = module.vpc.public_subnet_ids +} + +output "private_subnets" { + value = module.vpc.private_subnet_ids +} + + +output "availability_zones" { + value = module.vpc.availability_zones +} + + +output "vpc_cidr_block" { + value = module.vpc.vpc_cidr_blocks +} + +output "postgres_host" { + value = { for k, v in module.tr_component_postgres.postgres_endpoints : k => v} +} + + + + + + + +output "postgres_password" { + value = { for k, v in module.tr_component_postgres.db_password : k => v} + sensitive = true +} + + + + + + + + + + + + diff --git a/.terrarium-output/src/postgres.tf b/.terrarium-output/src/postgres.tf new file mode 100644 index 00000000..6085fcfc --- /dev/null +++ b/.terrarium-output/src/postgres.tf @@ -0,0 +1,11 @@ +module "tr_component_postgres" { + source = "../modules/postgres" + + + postgres_app_config = local.tr_component_postgres + + postgres_subnet_ids = module.vpc.database_subnets + postgres_vpc_id = module.vpc.vpc_id + postgres_cidr_blocks = module.vpc.vpc_cidr_blocks + postgres_database_subnet_group = module.vpc.database_subnet_group +} diff --git a/.terrarium-output/src/tr_gen_locals.tf b/.terrarium-output/src/tr_gen_locals.tf new file mode 100644 index 00000000..e88e01a8 --- /dev/null +++ b/.terrarium-output/src/tr_gen_locals.tf @@ -0,0 +1,18 @@ +locals { + tr_component_postgres = { + ledgerdb = { + family = "postgres" + version = "11.20" + } + } + tr_component_service_private = {} + tr_component_service_web = { + banking_app = { + cpu = 1024 + memory = 2048 + path = "/" + port = 3000 + protocol = "tcp" + } + } +} diff --git a/.terrarium-output/src/variables.tf b/.terrarium-output/src/variables.tf new file mode 100644 index 00000000..053081e6 --- /dev/null +++ b/.terrarium-output/src/variables.tf @@ -0,0 +1,85 @@ +variable "vpc_config" { + description = "A map of objects that define the VPC to create." + type = any + default = { + "default" = { + "vpc_cidr_block" = "10.0.0.0/16", + "number_of_azs" = 2 + } + } + +} + +variable "lb_config" { + description = "A map of objects that define the LB to create." + type = any + default = { + "default" = { + "load_balancer_type" = "application", + "create_alb" = true + } + } +} + +variable "common_name_prefix" { + description = "The common name prefix to use for all resources." + type = string + default = "terrarium" +} + +variable "environment" { + description = "The environment to deploy to." + type = string + default = "testing" +} + +variable "ecs_config" { + description = "A map of objects that define the ECS cluster to create." + type = any + default = { + "default" : { + name : "default", + engine : { + type : "FARGATE", + default_weight : 50, + spot_weight : 50, + } + } + } +} + + + + + + + + + + + + + + + + + + + + + + + + + +variable "domain_name" { + description = "The DNS zone domain name to create records in." + type = string + default = "platform.test.codepipes.io" +} + +variable "zone_id" { + description = "The DNS zone ID to create records in." + type = string + default = "placeholder" +} diff --git a/.terrarium-output/src/vpc.tf b/.terrarium-output/src/vpc.tf new file mode 100644 index 00000000..49016ed1 --- /dev/null +++ b/.terrarium-output/src/vpc.tf @@ -0,0 +1,11 @@ +module "vpc" { + source = "../modules/vpc" + + tr_component_vpc = var.vpc_config + + #Common Variables + environment = var.environment + common_name_prefix = var.common_name_prefix + extract_resource_name = local.extract_resource_name + +}