From 9eb9f4c4f58ccdaef4858bf92a05cd91b3ac5d8c Mon Sep 17 00:00:00 2001 From: riqardos Date: Mon, 29 Jan 2024 09:59:33 +0100 Subject: [PATCH 1/9] fix route53 --- README.md | 4 ++-- route53.tf | 2 +- variables.tf | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index ee27d2c..0f7431c 100644 --- a/README.md +++ b/README.md @@ -122,7 +122,7 @@ See [Production example](examples/production/README.md) for further information. | [alb\_ingress\_security\_group\_rules](#input\_alb\_ingress\_security\_group\_rules) | Application Load Balancer security group ingress rules |
map(object({
ip_protocol = string
from_port = optional(number)
to_port = optional(number)
referenced_security_group_id = optional(string)
description = optional(string)
cidr_ipv4 = optional(string)
cidr_ipv6 = optional(string)
}))
|
{
"all_http": {
"cidr_ipv4": "0.0.0.0/0",
"from_port": 80,
"ip_protocol": "tcp",
"to_port": 80
},
"all_https": {
"cidr_ipv4": "0.0.0.0/0",
"from_port": 443,
"ip_protocol": "tcp",
"to_port": 443
}
}
| no | | [attributes](#input\_attributes) | Additional attributes (e.g. `1`) | `list(string)` | `[]` | no | | [availability\_zones](#input\_availability\_zones) | List of availability zones | `list(string)` |
[
"eu-central-1a",
"eu-central-1b",
"eu-central-1c"
]
| no | -| [certificate\_arn](#input\_certificate\_arn) | Certificate ARN, if not set, certificate will be automatically created using '*.', `zone_id` must be set | `string` | `null` | no | +| [certificate\_arn](#input\_certificate\_arn) | Certificate ARN, if not set, certificate will be automatically created using '*.' | `string` | `null` | no | | [document\_db](#input\_document\_db) | DocumentDB configuration object |
object({
cluster_size = optional(number, 1)
cluster_family = optional(string, "docdb5.0")
instance_class = optional(string, "db.t4g.medium")
engine_version = optional(string, "5.0.0")
cluster_parameters = optional(list(object({
apply_method = string
name = string
value = string
})), [])
})
| `{}` | no | | [ecs\_autoscaling\_config](#input\_ecs\_autoscaling\_config) | n/a | `any` |
{
"on_demand": {
"capacity_provider": {
"default_capacity_provider_strategy": {
"base": 1,
"weight": 10
},
"maximum_scaling_step_size": 5,
"minimum_scaling_step_size": 1,
"target_capacity": 100
},
"instance_type": "m5.large",
"max_size": 6,
"min_size": 1,
"mixed_instances_policy": {
"instances_distribution": {
"on_demand_allocation_strategy": "prioritized",
"on_demand_base_capacity": 1,
"on_demand_percentage_above_base_capacity": 100,
"spot_allocation_strategy": "lowest-price"
},
"override": [
{
"instance_type": "m5.large",
"weighted_capacity": "1"
},
{
"instance_type": "c5.large",
"weighted_capacity": "1"
}
]
},
"use_mixed_instances_policy": true
},
"spot": {
"capacity_provider": {
"default_capacity_provider_strategy": {
"base": 0,
"weight": 80
},
"maximum_scaling_step_size": 5,
"minimum_scaling_step_size": 1,
"target_capacity": 100
},
"instance_type": "m5.large",
"max_size": 6,
"min_size": 1,
"mixed_instances_policy": {
"instances_distribution": {
"on_demand_allocation_strategy": "prioritized",
"on_demand_base_capacity": 0,
"on_demand_percentage_above_base_capacity": 0,
"spot_allocation_strategy": "lowest-price"
},
"override": [
{
"instance_type": "m5.large",
"weighted_capacity": "1"
},
{
"instance_type": "c5.large",
"weighted_capacity": "1"
}
]
},
"use_mixed_instances_policy": true
}
}
| no | | [ecs\_cluster\_config](#input\_ecs\_cluster\_config) | Cluster configuration object `execute_command_configuration`, see more [terraform docs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecs_cluster) | `any` |
{
"log_configuration": {
"cloud_watch_log_group_name": "/aws/ecs/aws-ec2"
},
"logging": "OVERRIDE"
}
| no | @@ -146,7 +146,7 @@ See [Production example](examples/production/README.md) for further information. | [stage](#input\_stage) | Stage, e.g. 'prod', 'staging', 'dev' | `string` | `""` | no | | [tags](#input\_tags) | Additional tags (e.g. `map('BusinessUnit','XYZ')` | `map(string)` | `{}` | no | | [vpc\_config](#input\_vpc\_config) | VPC configuration, ignored if `external_vpc` is set |
object({
ipv4_primary_cidr_block = string
availability_zones = list(string)
})
|
{
"availability_zones": [
"eu-central-1a",
"eu-central-1b",
"eu-central-1c"
],
"ipv4_primary_cidr_block": "10.0.0.0/16"
}
| no | -| [zone\_id](#input\_zone\_id) | Route53 DNS zone ID, if not set AWS route53 will be not used | `string` | `""` | no | +| [zone\_id](#input\_zone\_id) | Route53 DNS zone ID, if not set AWS route53 will be not used | `string` | `null` | no | ## Outputs diff --git a/route53.tf b/route53.tf index 1a3537b..d4b5a3a 100644 --- a/route53.tf +++ b/route53.tf @@ -27,7 +27,7 @@ resource "aws_acm_certificate" "alb" { resource "aws_route53_record" "cert_alb" { for_each = { - for dvo in aws_acm_certificate.alb[0].domain_validation_options : dvo.domain_name => { + for dvo in try(aws_acm_certificate.alb[0].domain_validation_options, []) : dvo.domain_name => { name = dvo.resource_record_name record = dvo.resource_record_value type = dvo.resource_record_type diff --git a/variables.tf b/variables.tf index 81c15f4..f6c50d1 100644 --- a/variables.tf +++ b/variables.tf @@ -42,7 +42,7 @@ variable "availability_zones" { variable "zone_id" { type = string - default = "" + default = null description = "Route53 DNS zone ID, if not set AWS route53 will be not used" } @@ -54,7 +54,7 @@ variable "root_dns_name" { variable "certificate_arn" { type = string default = null - description = "Certificate ARN, if not set, certificate will be automatically created using '*.', `zone_id` must be set" + description = "Certificate ARN, if not set, certificate will be automatically created using '*.'" } variable "document_db" { From d3d2c83b3a1381181d9fbb9d7a405c625d8c548f Mon Sep 17 00:00:00 2001 From: riqardos Date: Mon, 29 Jan 2024 10:30:45 +0100 Subject: [PATCH 2/9] fix tls certs for engine and quota --- examples/development/main.tf | 6 +++++- locals.tf | 8 ++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/examples/development/main.tf b/examples/development/main.tf index 9398795..4df6b80 100644 --- a/examples/development/main.tf +++ b/examples/development/main.tf @@ -151,7 +151,11 @@ module "appmixer_module" { ecs_per_service_config = { engine = { entrypoint = ["/bin/bash", "-c"] - command = ["apt-get update; apt-get install wget; wget https://truststore.pki.rds.amazonaws.com/global/global-bundle.pem; node gridd.js --http --emails"] + command = ["apt-get update; apt-get -y install wget; wget https://truststore.pki.rds.amazonaws.com/global/global-bundle.pem; node gridd.js --http --emails"] + } + quota = { + entrypoint = ["/bin/bash", "-c"] + command = ["apt-get update; apt-get -y install wget; wget -O /root//global-bundle.pem https://truststore.pki.rds.amazonaws.com/global/global-bundle.pem; npm start"] } } } diff --git a/locals.tf b/locals.tf index 9597d97..6ff2c60 100644 --- a/locals.tf +++ b/locals.tf @@ -105,8 +105,12 @@ locals { # Quota service configuration quota = { - image = "registry.appmixer.com/appmixer-quota:5.2.0" - env = {} + image = "registry.appmixer.com/appmixer-quota:5.2.0" + env = { + DB_TLS_CA_FILE = "/root/global-bundle.pem" + DB_USE_TLS = "true" + DB_SSL_VALIDATE = "true" + } cpu = 256 memory = 512 } From d128b993c7781dc9ddf30fbdeeda0700d3bbe2de Mon Sep 17 00:00:00 2001 From: riqardos Date: Mon, 29 Jan 2024 10:31:48 +0100 Subject: [PATCH 3/9] documentDb fix primary --- locals.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locals.tf b/locals.tf index 6ff2c60..6579022 100644 --- a/locals.tf +++ b/locals.tf @@ -17,7 +17,7 @@ locals { password = random_password.elasticsearch_password.result } - documentdb = var.external_documentdb != null ? var.external_documentdb : "mongodb://${module.documentdb_cluster.master_username}:${module.documentdb_cluster.master_password}@${module.documentdb_cluster.endpoint}:27017/appmixer?replicaSet=rs0&readPreference=secondaryPreferred&retryWrites=false" + documentdb = var.external_documentdb != null ? var.external_documentdb : "mongodb://${module.documentdb_cluster.master_username}:${module.documentdb_cluster.master_password}@${module.documentdb_cluster.endpoint}:27017/appmixer?replicaSet=rs0&readPreference=primaryPreferred&retryWrites=false" ## Services default configuration From e2c2d029bc0dd2bb5da7a230ad2a203d9580da29 Mon Sep 17 00:00:00 2001 From: riqardos Date: Mon, 29 Jan 2024 13:06:33 +0100 Subject: [PATCH 4/9] update dev setup --- examples/development/main.tf | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/examples/development/main.tf b/examples/development/main.tf index 4df6b80..4c97f47 100644 --- a/examples/development/main.tf +++ b/examples/development/main.tf @@ -59,14 +59,7 @@ module "appmixer_module" { enable_deletion_protection = false - elasticache = { - parameter = [ - { - name = "notify-keyspace-events" - value = "lK" - } - ] - } + ecs_autoscaling_config = { on_demand = { instance_type = "m5.large" @@ -158,6 +151,28 @@ module "appmixer_module" { command = ["apt-get update; apt-get -y install wget; wget -O /root//global-bundle.pem https://truststore.pki.rds.amazonaws.com/global/global-bundle.pem; npm start"] } } + + elasticsearch = { + instance_count = 1 + } + + elasticache = { + cluster_size = 1 + parameter = [ + { + name = "notify-keyspace-events" + value = "lK" + } + ] + } + + document_db = { + cluster_size = 1 + } + + rabbitmq = { + deployment_mode = "SINGLE_INSTANCE" + } } output "appmixer_module" { From de8f0821db194d3abe898bd3d904667bc580c26d Mon Sep 17 00:00:00 2001 From: riqardos Date: Mon, 29 Jan 2024 13:13:12 +0100 Subject: [PATCH 5/9] update prod setup + readme --- README.md | 18 ++++++++++++++++-- examples/production/main.tf | 8 +++++++- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 0f7431c..d5955a9 100644 --- a/README.md +++ b/README.md @@ -41,8 +41,22 @@ Admin user is initialized through automated task running in ECS. Variable `init_ ## Examples -See [Development example](examples/development/README.md) for further information. (Minimal setup) -See [Production example](examples/production/README.md) for further information. (High available setup) +- See [Development example](examples/development/README.md) for further information. (Minimal setup) +- See [Production example](examples/production/README.md) for further information. (High available setup) + +## Pricing +[AWS Pricing estimation](https://calculator.aws/#/estimate?id=e7ef36a5f2d80b239f143a9b0fb4dfaf935e00d4) for each environemnt. +Pricing can vary based on real traffic. + +- Development ~ 450$/month +- Production ~ 900$/month + +## Simple VPN +You can use [sshuttle](https://github.com/sshuttle/sshuttle) to connect to private VPC network. +1. Run EC2 with configured SSH keys in your public subnet +2. Install `sshuttle` +3. Connect through `sshuttle` to your running EC2 (`sshuttle -r 0.0.0.0/0 -vv`) +4. Your network traffic should be routed through EC2, you should be able to see managed AWS services running in private subnets. ## Architecture diff --git a/examples/production/main.tf b/examples/production/main.tf index c11d4f9..38bf12f 100644 --- a/examples/production/main.tf +++ b/examples/production/main.tf @@ -117,12 +117,18 @@ module "appmixer_module" { } elasticache = { - cluster_size = length(local.availability_zones) + cluster_size = 2 + instance_type = "cache.t3.medium" } document_db = { cluster_size = length(local.availability_zones) } + rabbitmq = { + deployment_mode = "CLUSTER_MULTI_AZ" + host_instance_type = "mq.m5.large" + } + } From e4b7aede49c6e18f6cd2567490e8c2bb2f06f58e Mon Sep 17 00:00:00 2001 From: riqardos Date: Mon, 29 Jan 2024 13:18:00 +0100 Subject: [PATCH 6/9] state backend example --- examples/development/providers.tf | 12 ++++++++++++ examples/production/providers.tf | 12 ++++++++++++ 2 files changed, 24 insertions(+) diff --git a/examples/development/providers.tf b/examples/development/providers.tf index e1bf6a7..fe2193c 100644 --- a/examples/development/providers.tf +++ b/examples/development/providers.tf @@ -1,3 +1,15 @@ +# Good practice is to use s3 bucket to save the state and dynamodb table as lock to restrict access to the state +# For example: +# +# terraform { +# backend "s3" { +# bucket = "appmixer-terraform-state" +# key = "appmixer/terraform.tfstate" +# region = "eu-central-1" +# } +# } + + provider "aws" { region = "eu-central-1" } diff --git a/examples/production/providers.tf b/examples/production/providers.tf index e1bf6a7..fe2193c 100644 --- a/examples/production/providers.tf +++ b/examples/production/providers.tf @@ -1,3 +1,15 @@ +# Good practice is to use s3 bucket to save the state and dynamodb table as lock to restrict access to the state +# For example: +# +# terraform { +# backend "s3" { +# bucket = "appmixer-terraform-state" +# key = "appmixer/terraform.tfstate" +# region = "eu-central-1" +# } +# } + + provider "aws" { region = "eu-central-1" } From a9cbf019f37f7f0f612173834cdf8ecf95a87c5e Mon Sep 17 00:00:00 2001 From: riqardos Date: Mon, 29 Jan 2024 13:27:13 +0100 Subject: [PATCH 7/9] update pricing link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d5955a9..dd864ae 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ Admin user is initialized through automated task running in ECS. Variable `init_ - See [Production example](examples/production/README.md) for further information. (High available setup) ## Pricing -[AWS Pricing estimation](https://calculator.aws/#/estimate?id=e7ef36a5f2d80b239f143a9b0fb4dfaf935e00d4) for each environemnt. +[AWS Pricing estimation](https://calculator.aws/#/estimate?id=255bd4b7f28f0aadd6dcc8e746b475ea18fd2779) for each environemnt. Pricing can vary based on real traffic. - Development ~ 450$/month From 8815e62cae2f850d8d380bfa1c099ef709ef7f3f Mon Sep 17 00:00:00 2001 From: riqardos Date: Mon, 29 Jan 2024 13:35:03 +0100 Subject: [PATCH 8/9] fix ECS_ENGINE_AUTH_DATA to make it optional --- ecs-ec2.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ecs-ec2.tf b/ecs-ec2.tf index 1af8812..3901735 100644 --- a/ecs-ec2.tf +++ b/ecs-ec2.tf @@ -49,7 +49,7 @@ locals { ECS_ENABLE_HIGH_DENSITY_ENI=true ECS_ENABLE_SPOT_INSTANCE_DRAINING=true ECS_ENGINE_AUTH_TYPE=dockercfg - ECS_ENGINE_AUTH_DATA=${sensitive(base64decode(var.ecs_registry_auth_data))} # pragma: allowlist secret + ECS_ENGINE_AUTH_DATA=${sensitive(try(base64decode(var.ecs_registry_auth_data), ""))} # pragma: allowlist secret EOF EOT } From d91807001e85d20e0b718ae332fb5a1a2ab0840f Mon Sep 17 00:00:00 2001 From: riqardos Date: Mon, 29 Jan 2024 14:30:10 +0100 Subject: [PATCH 9/9] update prod example --- examples/production/README.md | 4 +++- examples/production/main.tf | 8 ++++++-- route53.tf | 4 ++-- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/examples/production/README.md b/examples/production/README.md index b058969..176ef57 100644 --- a/examples/production/README.md +++ b/examples/production/README.md @@ -18,7 +18,9 @@ The code in this example shows how to use the module with production configurati ## Resources -No resources. +| Name | Type | +|------|------| +| [aws_route53_zone.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/route53_zone) | data source | ## Inputs diff --git a/examples/production/main.tf b/examples/production/main.tf index 38bf12f..ae8bd3b 100644 --- a/examples/production/main.tf +++ b/examples/production/main.tf @@ -5,6 +5,10 @@ locals { availability_zones = ["eu-central-1a", "eu-central-1b", "eu-central-1c"] } +data "aws_route53_zone" "this" { + name = "appmixer.co" +} + module "appmixer_module" { source = "../../" @@ -12,8 +16,8 @@ module "appmixer_module" { namespace = local.namespace environment = local.environment - root_dns_name = "ecs.appmixer.co" - zone_id = "XXX" + root_dns_name = "ecs-prod.appmixer.co" + zone_id = data.aws_route53_zone.this.zone_id vpc_config = { ipv4_primary_cidr_block = "10.0.0.0/16" diff --git a/route53.tf b/route53.tf index d4b5a3a..2569972 100644 --- a/route53.tf +++ b/route53.tf @@ -27,11 +27,11 @@ resource "aws_acm_certificate" "alb" { resource "aws_route53_record" "cert_alb" { for_each = { - for dvo in try(aws_acm_certificate.alb[0].domain_validation_options, []) : dvo.domain_name => { + for dvo in local.acm_certificate_enabled ? aws_acm_certificate.alb[0].domain_validation_options : [] : dvo.domain_name => { name = dvo.resource_record_name record = dvo.resource_record_value type = dvo.resource_record_type - } if local.acm_certificate_enabled + } } allow_overwrite = true