Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Module fix #5

Merged
merged 9 commits into from
Feb 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 18 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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=255bd4b7f28f0aadd6dcc8e746b475ea18fd2779) 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 <SSH_CONFIG> 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
Expand Down Expand Up @@ -122,7 +136,7 @@ See [Production example](examples/production/README.md) for further information.
| <a name="input_alb_ingress_security_group_rules"></a> [alb\_ingress\_security\_group\_rules](#input\_alb\_ingress\_security\_group\_rules) | Application Load Balancer security group ingress rules | <pre>map(object({<br> ip_protocol = string<br> from_port = optional(number)<br> to_port = optional(number)<br> referenced_security_group_id = optional(string)<br> description = optional(string)<br> cidr_ipv4 = optional(string)<br> cidr_ipv6 = optional(string)<br> }))</pre> | <pre>{<br> "all_http": {<br> "cidr_ipv4": "0.0.0.0/0",<br> "from_port": 80,<br> "ip_protocol": "tcp",<br> "to_port": 80<br> },<br> "all_https": {<br> "cidr_ipv4": "0.0.0.0/0",<br> "from_port": 443,<br> "ip_protocol": "tcp",<br> "to_port": 443<br> }<br>}</pre> | no |
| <a name="input_attributes"></a> [attributes](#input\_attributes) | Additional attributes (e.g. `1`) | `list(string)` | `[]` | no |
| <a name="input_availability_zones"></a> [availability\_zones](#input\_availability\_zones) | List of availability zones | `list(string)` | <pre>[<br> "eu-central-1a",<br> "eu-central-1b",<br> "eu-central-1c"<br>]</pre> | no |
| <a name="input_certificate_arn"></a> [certificate\_arn](#input\_certificate\_arn) | Certificate ARN, if not set, certificate will be automatically created using '*.<root\_dns\_name>', `zone_id` must be set | `string` | `null` | no |
| <a name="input_certificate_arn"></a> [certificate\_arn](#input\_certificate\_arn) | Certificate ARN, if not set, certificate will be automatically created using '*.<root\_dns\_name>' | `string` | `null` | no |
| <a name="input_document_db"></a> [document\_db](#input\_document\_db) | DocumentDB configuration object | <pre>object({<br> cluster_size = optional(number, 1)<br> cluster_family = optional(string, "docdb5.0")<br> instance_class = optional(string, "db.t4g.medium")<br> engine_version = optional(string, "5.0.0")<br> cluster_parameters = optional(list(object({<br> apply_method = string<br> name = string<br> value = string<br> })), [])<br> })</pre> | `{}` | no |
| <a name="input_ecs_autoscaling_config"></a> [ecs\_autoscaling\_config](#input\_ecs\_autoscaling\_config) | n/a | `any` | <pre>{<br> "on_demand": {<br> "capacity_provider": {<br> "default_capacity_provider_strategy": {<br> "base": 1,<br> "weight": 10<br> },<br> "maximum_scaling_step_size": 5,<br> "minimum_scaling_step_size": 1,<br> "target_capacity": 100<br> },<br> "instance_type": "m5.large",<br> "max_size": 6,<br> "min_size": 1,<br> "mixed_instances_policy": {<br> "instances_distribution": {<br> "on_demand_allocation_strategy": "prioritized",<br> "on_demand_base_capacity": 1,<br> "on_demand_percentage_above_base_capacity": 100,<br> "spot_allocation_strategy": "lowest-price"<br> },<br> "override": [<br> {<br> "instance_type": "m5.large",<br> "weighted_capacity": "1"<br> },<br> {<br> "instance_type": "c5.large",<br> "weighted_capacity": "1"<br> }<br> ]<br> },<br> "use_mixed_instances_policy": true<br> },<br> "spot": {<br> "capacity_provider": {<br> "default_capacity_provider_strategy": {<br> "base": 0,<br> "weight": 80<br> },<br> "maximum_scaling_step_size": 5,<br> "minimum_scaling_step_size": 1,<br> "target_capacity": 100<br> },<br> "instance_type": "m5.large",<br> "max_size": 6,<br> "min_size": 1,<br> "mixed_instances_policy": {<br> "instances_distribution": {<br> "on_demand_allocation_strategy": "prioritized",<br> "on_demand_base_capacity": 0,<br> "on_demand_percentage_above_base_capacity": 0,<br> "spot_allocation_strategy": "lowest-price"<br> },<br> "override": [<br> {<br> "instance_type": "m5.large",<br> "weighted_capacity": "1"<br> },<br> {<br> "instance_type": "c5.large",<br> "weighted_capacity": "1"<br> }<br> ]<br> },<br> "use_mixed_instances_policy": true<br> }<br>}</pre> | no |
| <a name="input_ecs_cluster_config"></a> [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` | <pre>{<br> "log_configuration": {<br> "cloud_watch_log_group_name": "/aws/ecs/aws-ec2"<br> },<br> "logging": "OVERRIDE"<br>}</pre> | no |
Expand All @@ -146,7 +160,7 @@ See [Production example](examples/production/README.md) for further information.
| <a name="input_stage"></a> [stage](#input\_stage) | Stage, e.g. 'prod', 'staging', 'dev' | `string` | `""` | no |
| <a name="input_tags"></a> [tags](#input\_tags) | Additional tags (e.g. `map('BusinessUnit','XYZ')` | `map(string)` | `{}` | no |
| <a name="input_vpc_config"></a> [vpc\_config](#input\_vpc\_config) | VPC configuration, ignored if `external_vpc` is set | <pre>object({<br> ipv4_primary_cidr_block = string<br> availability_zones = list(string)<br> })</pre> | <pre>{<br> "availability_zones": [<br> "eu-central-1a",<br> "eu-central-1b",<br> "eu-central-1c"<br> ],<br> "ipv4_primary_cidr_block": "10.0.0.0/16"<br>}</pre> | no |
| <a name="input_zone_id"></a> [zone\_id](#input\_zone\_id) | Route53 DNS zone ID, if not set AWS route53 will be not used | `string` | `""` | no |
| <a name="input_zone_id"></a> [zone\_id](#input\_zone\_id) | Route53 DNS zone ID, if not set AWS route53 will be not used | `string` | `null` | no |

## Outputs

Expand Down
2 changes: 1 addition & 1 deletion ecs-ec2.tf
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
37 changes: 28 additions & 9 deletions examples/development/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -151,9 +144,35 @@ 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"]
}
}

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" {
Expand Down
12 changes: 12 additions & 0 deletions examples/development/providers.tf
Original file line number Diff line number Diff line change
@@ -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"
}
4 changes: 3 additions & 1 deletion examples/production/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
16 changes: 13 additions & 3 deletions examples/production/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,19 @@ locals {
availability_zones = ["eu-central-1a", "eu-central-1b", "eu-central-1c"]
}

data "aws_route53_zone" "this" {
name = "appmixer.co"
}

module "appmixer_module" {
source = "../../"

name = local.name
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"
Expand Down Expand Up @@ -117,12 +121,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"
}


}
12 changes: 12 additions & 0 deletions examples/production/providers.tf
Original file line number Diff line number Diff line change
@@ -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"
}
10 changes: 7 additions & 3 deletions locals.tf
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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
}
Expand Down
4 changes: 2 additions & 2 deletions route53.tf
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,11 @@ 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 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
Expand Down
4 changes: 2 additions & 2 deletions variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}

Expand All @@ -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 '*.<root_dns_name>', `zone_id` must be set"
description = "Certificate ARN, if not set, certificate will be automatically created using '*.<root_dns_name>'"
}

variable "document_db" {
Expand Down