Customer terraform (#9136)

* Initial work on customer terraform modules.

I'm getting lost so I'll need to start applying stuff to make sure it
works

* Stopping here for now

Next I need to add optional()'s to everything so we can specify partial
structure.https://developer.hashicorp.com/terraform/language/expressions/type-constraints#optional-object-type-attributes

* A random check in

Need to redo basically all variables and fix everything

* Got a lot more working finally!

* RDS and Elasticache now create

* Clean apply, just need debugging

* Should be fully working, just need to make a fully working example

* Everything is working and added a usage example

* Added contributing

* fixup

* Final wiring changes and ran the autodoc command

* Add more docs

* Fixup
This commit is contained in:
Zachary Winnerman 2022-12-29 16:28:50 -05:00 committed by GitHub
parent 1b47f9e700
commit 6f4a20bdcd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 1574 additions and 0 deletions

21
terraform/.header.md Normal file
View file

@ -0,0 +1,21 @@
This module provides a basic Fleet setup. This assumes that you bring nothing to the installation.
If you want to bring your own VPC/database/cache nodes/ECS cluster, then use one of the submodules provided.
The following is the module layout so you can navigate to the module that you want:
* Root module (use this to get a Fleet instance ASAP with minimal setup)
* BYO-VPC (use this if you want to install Fleet inside an existing VPC)
* BYO-database (use this if you want to use an existing database and cache node)
* BYO-ECS (use this if you want to bring your own everything but Fleet ECS services)
# How to improve this module
If this module somehow doesn't fit your needs, feel free to contact us by
opening a ticket, or contacting your contact at Fleet. Our goal is to make this module
fit all needs within AWS, so we will try to find a solution so that this module fits your needs.
If you want to make the changes yourself, simply make a PR into main with your additions.
We would ask that you make sure that variables are defined as null if there is
no default that makes sense and that variable changes are reflected all the way up the stack.
# How to update this readme
Edit .header.md and run `terraform-docs markdown . > README.md`

View file

@ -0,0 +1 @@
header-from: .header.md

59
terraform/README.md Normal file
View file

@ -0,0 +1,59 @@
This module provides a basic Fleet setup. This assumes that you bring nothing to the installation.
If you want to bring your own VPC/database/cache nodes/ECS cluster, then use one of the submodules provided.
The following is the module layout so you can navigate to the module that you want:
* Root module (use this to get a Fleet instance ASAP with minimal setup)
* BYO-VPC (use this if you want to install Fleet inside an existing VPC)
* BYO-database (use this if you want to use an existing database and cache node)
* BYO-ECS (use this if you want to bring your own everything but Fleet ECS services)
# How to improve this module
If this module somehow doesn't fit your needs, feel free to contact us by
opening a ticket, or contacting your contact at Fleet. Our goal is to make this module
fit all needs within AWS, so we will try to find a solution so that this module fits your needs.
If you want to make the changes yourself, simply make a PR into main with your additions.
We would ask that you make sure that variables are defined as null if there is
no default that makes sense and that variable changes are reflected all the way up the stack.
# How to update this readme
Edit .header.md and run `terraform-docs markdown . > README.md`
## Requirements
No requirements.
## Providers
No providers.
## Modules
| Name | Source | Version |
|------|--------|---------|
| <a name="module_byo-vpc"></a> [byo-vpc](#module\_byo-vpc) | ./byo-vpc | n/a |
| <a name="module_vpc"></a> [vpc](#module\_vpc) | terraform-aws-modules/vpc/aws | n/a |
## Resources
No resources.
## Inputs
| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| <a name="input_alb_config"></a> [alb\_config](#input\_alb\_config) | n/a | <pre>object({<br> name = optional(string, "fleet")<br> security_groups = optional(list(string), [])<br> access_logs = optional(map(string), {})<br> })</pre> | `{}` | no |
| <a name="input_certificate_arn"></a> [certificate\_arn](#input\_certificate\_arn) | n/a | `string` | n/a | yes |
| <a name="input_ecs_cluster"></a> [ecs\_cluster](#input\_ecs\_cluster) | The config for the terraform-aws-modules/ecs/aws module | <pre>object({<br> autoscaling_capacity_providers = any<br> cluster_configuration = any<br> cluster_name = string<br> cluster_settings = map(string)<br> create = bool<br> default_capacity_provider_use_fargate = bool<br> fargate_capacity_providers = any<br> tags = map(string)<br> })</pre> | <pre>{<br> "autoscaling_capacity_providers": {},<br> "cluster_configuration": {<br> "execute_command_configuration": {<br> "log_configuration": {<br> "cloud_watch_log_group_name": "/aws/ecs/aws-ec2"<br> },<br> "logging": "OVERRIDE"<br> }<br> },<br> "cluster_name": "fleet",<br> "cluster_settings": {<br> "name": "containerInsights",<br> "value": "enabled"<br> },<br> "create": true,<br> "default_capacity_provider_use_fargate": true,<br> "fargate_capacity_providers": {<br> "FARGATE": {<br> "default_capacity_provider_strategy": {<br> "weight": 100<br> }<br> },<br> "FARGATE_SPOT": {<br> "default_capacity_provider_strategy": {<br> "weight": 0<br> }<br> }<br> },<br> "tags": {}<br>}</pre> | no |
| <a name="input_fleet_config"></a> [fleet\_config](#input\_fleet\_config) | The configuration object for Fleet itself. Fields that default to null will have their respective resources created if not specified. | <pre>object({<br> mem = optional(number, 512)<br> cpu = optional(number, 256)<br> image = optional(string, "fleetdm/fleet:v4.22.1")<br> extra_environment_variables = optional(map(string), {})<br> extra_secrets = optional(map(string), {})<br> security_groups = optional(list(string), null)<br> iam_role_arn = optional(string, null)<br> database = object({<br> password_secret_arn = string<br> user = string<br> database = string<br> address = string<br> rr_address = optional(string, null)<br> })<br> redis = object({<br> address = string<br> use_tls = optional(bool, true)<br> })<br> awslogs = optional(object({<br> name = optional(string, null)<br> region = optional(string, null)<br> prefix = optional(string, "fleet")<br> retention = optional(number, 5)<br> }), {<br> name = null<br> region = null<br> prefix = "fleet"<br> retention = 5<br> })<br> loadbalancer = object({<br> arn = string<br> })<br> networking = object({<br> subnets = list(string)<br> security_groups = optional(list(string), null)<br> })<br> autoscaling = optional(object({<br> max_capacity = optional(number, 5)<br> min_capacity = optional(number, 1)<br> memory_tracking_target_value = optional(number, 80)<br> cpu_tracking_target_value = optional(number, 80)<br> }), {<br> max_capacity = 5<br> min_capacity = 1<br> memory_tracking_target_value = 80<br> cpu_tracking_target_value = 80<br> })<br> })</pre> | <pre>{<br> "autoscaling": {<br> "cpu_tracking_target_value": 80,<br> "max_capacity": 5,<br> "memory_tracking_target_value": 80,<br> "min_capacity": 1<br> },<br> "awslogs": {<br> "name": null,<br> "prefix": "fleet",<br> "region": null,<br> "retention": 5<br> },<br> "cpu": 256,<br> "database": {<br> "address": null,<br> "database": null,<br> "password_secret_arn": null,<br> "rr_address": null,<br> "user": null<br> },<br> "extra_environment_variables": {},<br> "extra_secrets": {},<br> "iam_role_arn": null,<br> "image": "fleetdm/fleet:v4.22.1",<br> "loadbalancer": {<br> "arn": null<br> },<br> "mem": 512,<br> "networking": {<br> "security_groups": null,<br> "subnets": null<br> },<br> "redis": {<br> "address": null,<br> "use_tls": true<br> },<br> "security_groups": null<br>}</pre> | no |
| <a name="input_migration_config"></a> [migration\_config](#input\_migration\_config) | The configuration object for Fleet's migration task. | <pre>object({<br> mem = number<br> cpu = number<br> })</pre> | <pre>{<br> "cpu": 1024,<br> "mem": 2048<br>}</pre> | no |
| <a name="input_rds_config"></a> [rds\_config](#input\_rds\_config) | The config for the terraform-aws-modules/rds-aurora/aws module | <pre>object({<br> name = optional(string, "fleet")<br> engine_version = optional(string, "8.0.mysql_aurora.3.02.2")<br> instance_class = optional(string, "db.t4g.large")<br> subnets = optional(list(string), [])<br> allowed_security_groups = optional(list(string), [])<br> allowed_cidr_blocks = optional(list(string), [])<br> apply_immediately = optional(bool, true)<br> monitoring_interval = optional(number, 10)<br> db_parameter_group_name = optional(string)<br> db_cluster_parameter_group_name = optional(string)<br> enabled_cloudwatch_logs_exports = optional(list(string), [])<br> master_username = optional(string, "fleet")<br> })</pre> | <pre>{<br> "allowed_cidr_blocks": [],<br> "allowed_security_groups": [],<br> "apply_immediately": true,<br> "db_cluster_parameter_group_name": null,<br> "db_parameter_group_name": null,<br> "enabled_cloudwatch_logs_exports": [],<br> "engine_version": "8.0.mysql_aurora.3.02.2",<br> "instance_class": "db.t4g.large",<br> "master_username": "fleet",<br> "monitoring_interval": 10,<br> "name": "fleet",<br> "subnets": []<br>}</pre> | no |
| <a name="input_redis_config"></a> [redis\_config](#input\_redis\_config) | n/a | <pre>object({<br> name = optional(string, "fleet")<br> replication_group_id = optional(string)<br> elasticache_subnet_group_name = optional(string)<br> allowed_security_group_ids = optional(list(string), [])<br> subnets = list(string)<br> availability_zones = list(string)<br> cluster_size = optional(number, 3)<br> instance_type = optional(string, "cache.m5.large")<br> apply_immediately = optional(bool, true)<br> automatic_failover_enabled = optional(bool, false)<br> engine_version = optional(string, "6.x")<br> family = optional(string, "redis6.x")<br> at_rest_encryption_enabled = optional(bool, true)<br> transit_encryption_enabled = optional(bool, true)<br> parameter = optional(list(object({<br> name = string<br> value = string<br> })), [])<br> })</pre> | <pre>{<br> "allowed_security_group_ids": [],<br> "apply_immediately": true,<br> "at_rest_encryption_enabled": true,<br> "automatic_failover_enabled": false,<br> "availability_zones": null,<br> "cluster_size": 3,<br> "elasticache_subnet_group_name": null,<br> "engine_version": "6.x",<br> "family": "redis6.x",<br> "instance_type": "cache.m5.large",<br> "name": "fleet",<br> "parameter": [],<br> "replication_group_id": null,<br> "subnets": null,<br> "transit_encryption_enabled": true<br>}</pre> | no |
| <a name="input_vpc"></a> [vpc](#input\_vpc) | n/a | <pre>object({<br> name = string<br> cidr = string<br> azs = list(string)<br> private_subnets = list(string)<br> public_subnets = list(string)<br> database_subnets = list(string)<br> elasticache_subnets = list(string)<br><br> create_database_subnet_group = bool<br> create_database_subnet_route_table = bool<br> create_elasticache_subnet_group = bool<br> create_elasticache_subnet_route_table = bool<br> enable_vpn_gateway = bool<br> one_nat_gateway_per_az = bool<br> single_nat_gateway = bool<br> enable_nat_gateway = bool<br> })</pre> | <pre>{<br> "azs": [<br> "us-east-2a",<br> "us-east-2b",<br> "us-east-2c"<br> ],<br> "cidr": "10.10.0.0/16",<br> "create_database_subnet_group": true,<br> "create_database_subnet_route_table": true,<br> "create_elasticache_subnet_group": true,<br> "create_elasticache_subnet_route_table": true,<br> "database_subnets": [<br> "10.10.21.0/24",<br> "10.10.22.0/24",<br> "10.10.23.0/24"<br> ],<br> "elasticache_subnets": [<br> "10.10.31.0/24",<br> "10.10.32.0/24",<br> "10.10.33.0/24"<br> ],<br> "enable_nat_gateway": true,<br> "enable_vpn_gateway": false,<br> "name": "fleet",<br> "one_nat_gateway_per_az": false,<br> "private_subnets": [<br> "10.10.1.0/24",<br> "10.10.2.0/24",<br> "10.10.3.0/24"<br> ],<br> "public_subnets": [<br> "10.10.11.0/24",<br> "10.10.12.0/24",<br> "10.10.13.0/24"<br> ],<br> "single_nat_gateway": true<br>}</pre> | no |
## Outputs
| Name | Description |
|------|-------------|
| <a name="output_byo-vpc"></a> [byo-vpc](#output\_byo-vpc) | n/a |

View file

@ -0,0 +1,46 @@
## Requirements
No requirements.
## Providers
| Name | Version |
|------|---------|
| <a name="provider_aws"></a> [aws](#provider\_aws) | 4.42.0 |
| <a name="provider_random"></a> [random](#provider\_random) | 3.4.3 |
## Modules
| Name | Source | Version |
|------|--------|---------|
| <a name="module_byo-db"></a> [byo-db](#module\_byo-db) | ./byo-db | n/a |
| <a name="module_rds"></a> [rds](#module\_rds) | terraform-aws-modules/rds-aurora/aws | 7.6.0 |
| <a name="module_redis"></a> [redis](#module\_redis) | cloudposse/elasticache-redis/aws | 0.48.0 |
| <a name="module_secrets-manager-1"></a> [secrets-manager-1](#module\_secrets-manager-1) | lgallard/secrets-manager/aws | 0.6.1 |
## Resources
| Name | Type |
|------|------|
| [aws_db_parameter_group.main](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/db_parameter_group) | resource |
| [aws_rds_cluster_parameter_group.main](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/rds_cluster_parameter_group) | resource |
| [random_password.rds](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/password) | resource |
| [aws_subnet.redis](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/subnet) | data source |
## Inputs
| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| <a name="input_alb_config"></a> [alb\_config](#input\_alb\_config) | n/a | <pre>object({<br> name = optional(string, "fleet")<br> subnets = list(string)<br> security_groups = optional(list(string), [])<br> access_logs = optional(map(string), {})<br> certificate_arn = string<br> })</pre> | n/a | yes |
| <a name="input_ecs_cluster"></a> [ecs\_cluster](#input\_ecs\_cluster) | The config for the terraform-aws-modules/ecs/aws module | <pre>object({<br> autoscaling_capacity_providers = any<br> cluster_configuration = any<br> cluster_name = string<br> cluster_settings = map(string)<br> create = bool<br> default_capacity_provider_use_fargate = bool<br> fargate_capacity_providers = any<br> tags = map(string)<br> })</pre> | <pre>{<br> "autoscaling_capacity_providers": {},<br> "cluster_configuration": {<br> "execute_command_configuration": {<br> "log_configuration": {<br> "cloud_watch_log_group_name": "/aws/ecs/aws-ec2"<br> },<br> "logging": "OVERRIDE"<br> }<br> },<br> "cluster_name": "fleet",<br> "cluster_settings": {<br> "name": "containerInsights",<br> "value": "enabled"<br> },<br> "create": true,<br> "default_capacity_provider_use_fargate": true,<br> "fargate_capacity_providers": {<br> "FARGATE": {<br> "default_capacity_provider_strategy": {<br> "weight": 100<br> }<br> },<br> "FARGATE_SPOT": {<br> "default_capacity_provider_strategy": {<br> "weight": 0<br> }<br> }<br> },<br> "tags": {}<br>}</pre> | no |
| <a name="input_fleet_config"></a> [fleet\_config](#input\_fleet\_config) | The configuration object for Fleet itself. Fields that default to null will have their respective resources created if not specified. | <pre>object({<br> mem = optional(number, 512)<br> cpu = optional(number, 256)<br> image = optional(string, "fleetdm/fleet:v4.22.1")<br> extra_environment_variables = optional(map(string), {})<br> extra_secrets = optional(map(string), {})<br> security_groups = optional(list(string), null)<br> iam_role_arn = optional(string, null)<br> database = object({<br> password_secret_arn = string<br> user = string<br> database = string<br> address = string<br> rr_address = optional(string, null)<br> })<br> redis = object({<br> address = string<br> use_tls = optional(bool, true)<br> })<br> awslogs = optional(object({<br> name = optional(string, null)<br> region = optional(string, null)<br> prefix = optional(string, "fleet")<br> retention = optional(number, 5)<br> }), {<br> name = null<br> region = null<br> prefix = "fleet"<br> retention = 5<br> })<br> loadbalancer = object({<br> arn = string<br> })<br> networking = object({<br> subnets = list(string)<br> security_groups = optional(list(string), null)<br> })<br> autoscaling = optional(object({<br> max_capacity = optional(number, 5)<br> min_capacity = optional(number, 1)<br> memory_tracking_target_value = optional(number, 80)<br> cpu_tracking_target_value = optional(number, 80)<br> }), {<br> max_capacity = 5<br> min_capacity = 1<br> memory_tracking_target_value = 80<br> cpu_tracking_target_value = 80<br> })<br> })</pre> | <pre>{<br> "autoscaling": {<br> "cpu_tracking_target_value": 80,<br> "max_capacity": 5,<br> "memory_tracking_target_value": 80,<br> "min_capacity": 1<br> },<br> "awslogs": {<br> "name": null,<br> "prefix": "fleet",<br> "region": null,<br> "retention": 5<br> },<br> "cpu": 256,<br> "database": {<br> "address": null,<br> "database": null,<br> "password_secret_arn": null,<br> "rr_address": null,<br> "user": null<br> },<br> "extra_environment_variables": {},<br> "extra_secrets": {},<br> "iam_role_arn": null,<br> "image": "fleetdm/fleet:v4.22.1",<br> "loadbalancer": {<br> "arn": null<br> },<br> "mem": 512,<br> "networking": {<br> "security_groups": null,<br> "subnets": null<br> },<br> "redis": {<br> "address": null,<br> "use_tls": true<br> },<br> "security_groups": null<br>}</pre> | no |
| <a name="input_migration_config"></a> [migration\_config](#input\_migration\_config) | The configuration object for Fleet's migration task. | <pre>object({<br> mem = number<br> cpu = number<br> })</pre> | <pre>{<br> "cpu": 1024,<br> "mem": 2048<br>}</pre> | no |
| <a name="input_rds_config"></a> [rds\_config](#input\_rds\_config) | The config for the terraform-aws-modules/rds-aurora/aws module | <pre>object({<br> name = optional(string, "fleet")<br> engine_version = optional(string, "8.0.mysql_aurora.3.02.2")<br> instance_class = optional(string, "db.t4g.large")<br> subnets = optional(list(string), [])<br> allowed_security_groups = optional(list(string), [])<br> allowed_cidr_blocks = optional(list(string), [])<br> apply_immediately = optional(bool, true)<br> monitoring_interval = optional(number, 10)<br> db_parameter_group_name = optional(string)<br> db_cluster_parameter_group_name = optional(string)<br> enabled_cloudwatch_logs_exports = optional(list(string), [])<br> master_username = optional(string, "fleet")<br> })</pre> | <pre>{<br> "allowed_cidr_blocks": [],<br> "allowed_security_groups": [],<br> "apply_immediately": true,<br> "db_cluster_parameter_group_name": null,<br> "db_parameter_group_name": null,<br> "enabled_cloudwatch_logs_exports": [],<br> "engine_version": "8.0.mysql_aurora.3.02.2",<br> "instance_class": "db.t4g.large",<br> "master_username": "fleet",<br> "monitoring_interval": 10,<br> "name": "fleet",<br> "subnets": []<br>}</pre> | no |
| <a name="input_redis_config"></a> [redis\_config](#input\_redis\_config) | n/a | <pre>object({<br> name = optional(string, "fleet")<br> replication_group_id = optional(string)<br> elasticache_subnet_group_name = optional(string)<br> allowed_security_group_ids = optional(list(string), [])<br> subnets = list(string)<br> availability_zones = list(string)<br> cluster_size = optional(number, 3)<br> instance_type = optional(string, "cache.m5.large")<br> apply_immediately = optional(bool, true)<br> automatic_failover_enabled = optional(bool, false)<br> engine_version = optional(string, "6.x")<br> family = optional(string, "redis6.x")<br> at_rest_encryption_enabled = optional(bool, true)<br> transit_encryption_enabled = optional(bool, true)<br> parameter = optional(list(object({<br> name = string<br> value = string<br> })), [])<br> })</pre> | <pre>{<br> "allowed_security_group_ids": [],<br> "apply_immediately": true,<br> "at_rest_encryption_enabled": true,<br> "automatic_failover_enabled": false,<br> "availability_zones": null,<br> "cluster_size": 3,<br> "elasticache_subnet_group_name": null,<br> "engine_version": "6.x",<br> "family": "redis6.x",<br> "instance_type": "cache.m5.large",<br> "name": "fleet",<br> "parameter": [],<br> "replication_group_id": null,<br> "subnets": null,<br> "transit_encryption_enabled": true<br>}</pre> | no |
| <a name="input_vpc_config"></a> [vpc\_config](#input\_vpc\_config) | n/a | <pre>object({<br> vpc_id = string<br> networking = object({<br> subnets = list(string)<br> })<br> })</pre> | n/a | yes |
## Outputs
| Name | Description |
|------|-------------|
| <a name="output_byo-db"></a> [byo-db](#output\_byo-db) | n/a |

View file

@ -0,0 +1,40 @@
## Requirements
No requirements.
## Providers
| Name | Version |
|------|---------|
| <a name="provider_aws"></a> [aws](#provider\_aws) | 4.40.0 |
## Modules
| Name | Source | Version |
|------|--------|---------|
| <a name="module_alb"></a> [alb](#module\_alb) | terraform-aws-modules/alb/aws | 8.2.1 |
| <a name="module_cluster"></a> [cluster](#module\_cluster) | terraform-aws-modules/ecs/aws | 4.1.2 |
| <a name="module_ecs"></a> [ecs](#module\_ecs) | ./byo-ecs | n/a |
## Resources
| Name | Type |
|------|------|
| [aws_security_group.alb](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group) | resource |
## Inputs
| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| <a name="input_alb_config"></a> [alb\_config](#input\_alb\_config) | n/a | <pre>object({<br> name = optional(string, "fleet")<br> subnets = list(string)<br> security_groups = optional(list(string), [])<br> access_logs = optional(map(string), {})<br> certificate_arn = string<br> })</pre> | n/a | yes |
| <a name="input_ecs_cluster"></a> [ecs\_cluster](#input\_ecs\_cluster) | The config for the terraform-aws-modules/ecs/aws module | <pre>object({<br> autoscaling_capacity_providers = any<br> cluster_configuration = any<br> cluster_name = string<br> cluster_settings = map(string)<br> create = bool<br> default_capacity_provider_use_fargate = bool<br> fargate_capacity_providers = any<br> tags = map(string)<br> })</pre> | <pre>{<br> "autoscaling_capacity_providers": {},<br> "cluster_configuration": {<br> "execute_command_configuration": {<br> "log_configuration": {<br> "cloud_watch_log_group_name": "/aws/ecs/aws-ec2"<br> },<br> "logging": "OVERRIDE"<br> }<br> },<br> "cluster_name": "fleet",<br> "cluster_settings": {<br> "name": "containerInsights",<br> "value": "enabled"<br> },<br> "create": true,<br> "default_capacity_provider_use_fargate": true,<br> "fargate_capacity_providers": {<br> "FARGATE": {<br> "default_capacity_provider_strategy": {<br> "weight": 100<br> }<br> },<br> "FARGATE_SPOT": {<br> "default_capacity_provider_strategy": {<br> "weight": 0<br> }<br> }<br> },<br> "tags": {}<br>}</pre> | no |
| <a name="input_fleet_config"></a> [fleet\_config](#input\_fleet\_config) | The configuration object for Fleet itself. Fields that default to null will have their respective resources created if not specified. | <pre>object({<br> mem = optional(number, 512)<br> cpu = optional(number, 256)<br> image = optional(string, "fleetdm/fleet:v4.22.1")<br> extra_environment_variables = optional(map(string), {})<br> extra_secrets = optional(map(string), {})<br> security_groups = optional(list(string), null)<br> iam_role_arn = optional(string, null)<br> database = object({<br> password_secret_arn = string<br> user = string<br> database = string<br> address = string<br> rr_address = optional(string, null)<br> })<br> redis = object({<br> address = string<br> use_tls = optional(bool, true)<br> })<br> awslogs = optional(object({<br> name = optional(string, null)<br> region = optional(string, null)<br> prefix = optional(string, "fleet")<br> retention = optional(number, 5)<br> }), {<br> name = null<br> region = null<br> prefix = "fleet"<br> retention = 5<br> })<br> loadbalancer = object({<br> arn = string<br> })<br> networking = object({<br> subnets = list(string)<br> security_groups = optional(list(string), null)<br> })<br> autoscaling = optional(object({<br> max_capacity = optional(number, 5)<br> min_capacity = optional(number, 1)<br> memory_tracking_target_value = optional(number, 80)<br> cpu_tracking_target_value = optional(number, 80)<br> }), {<br> max_capacity = 5<br> min_capacity = 1<br> memory_tracking_target_value = 80<br> cpu_tracking_target_value = 80<br> })<br> })</pre> | <pre>{<br> "autoscaling": {<br> "cpu_tracking_target_value": 80,<br> "max_capacity": 5,<br> "memory_tracking_target_value": 80,<br> "min_capacity": 1<br> },<br> "awslogs": {<br> "name": null,<br> "prefix": "fleet",<br> "region": null,<br> "retention": 5<br> },<br> "cpu": 256,<br> "database": {<br> "address": null,<br> "database": null,<br> "password_secret_arn": null,<br> "rr_address": null,<br> "user": null<br> },<br> "extra_environment_variables": {},<br> "extra_secrets": {},<br> "iam_role_arn": null,<br> "image": "fleetdm/fleet:v4.22.1",<br> "loadbalancer": {<br> "arn": null<br> },<br> "mem": 512,<br> "networking": {<br> "security_groups": null,<br> "subnets": null<br> },<br> "redis": {<br> "address": null,<br> "use_tls": true<br> },<br> "security_groups": null<br>}</pre> | no |
| <a name="input_migration_config"></a> [migration\_config](#input\_migration\_config) | The configuration object for Fleet's migration task. | <pre>object({<br> mem = number<br> cpu = number<br> })</pre> | <pre>{<br> "cpu": 1024,<br> "mem": 2048<br>}</pre> | no |
| <a name="input_vpc_id"></a> [vpc\_id](#input\_vpc\_id) | n/a | `string` | n/a | yes |
## Outputs
| Name | Description |
|------|-------------|
| <a name="output_alb"></a> [alb](#output\_alb) | n/a |
| <a name="output_byo-ecs"></a> [byo-ecs](#output\_byo-ecs) | n/a |

View file

@ -0,0 +1,51 @@
## Requirements
No requirements.
## Providers
| Name | Version |
|------|---------|
| <a name="provider_aws"></a> [aws](#provider\_aws) | 4.39.0 |
## Modules
No modules.
## Resources
| Name | Type |
|------|------|
| [aws_appautoscaling_policy.ecs_policy_cpu](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/appautoscaling_policy) | resource |
| [aws_appautoscaling_policy.ecs_policy_memory](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/appautoscaling_policy) | resource |
| [aws_appautoscaling_target.ecs_target](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/appautoscaling_target) | resource |
| [aws_cloudwatch_log_group.main](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_group) | resource |
| [aws_ecs_service.fleet](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecs_service) | resource |
| [aws_ecs_task_definition.backend](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecs_task_definition) | resource |
| [aws_iam_policy.execution](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource |
| [aws_iam_policy.main](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource |
| [aws_iam_role.execution](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource |
| [aws_iam_role.main](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource |
| [aws_iam_role_policy_attachment.execution](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource |
| [aws_iam_role_policy_attachment.main](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource |
| [aws_iam_role_policy_attachment.role_attachment](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource |
| [aws_security_group.main](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group) | resource |
| [aws_iam_policy_document.assume_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
| [aws_iam_policy_document.fleet](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
| [aws_iam_policy_document.fleet-execution](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
| [aws_region.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/region) | data source |
## Inputs
| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| <a name="input_ecs_cluster"></a> [ecs\_cluster](#input\_ecs\_cluster) | The name of the ECS cluster to use | `string` | n/a | yes |
| <a name="input_fleet_config"></a> [fleet\_config](#input\_fleet\_config) | The configuration object for Fleet itself. Fields that default to null will have their respective resources created if not specified. | <pre>object({<br> mem = optional(number, 512)<br> cpu = optional(number, 256)<br> image = optional(string, "fleetdm/fleet:v4.22.1")<br> extra_environment_variables = optional(map(string), {})<br> extra_secrets = optional(map(string), {})<br> security_groups = optional(list(string), null)<br> iam_role_arn = optional(string, null)<br> database = object({<br> password_secret_arn = string<br> user = string<br> database = string<br> address = string<br> rr_address = optional(string, null)<br> })<br> redis = object({<br> address = string<br> use_tls = optional(bool, true)<br> })<br> awslogs = optional(object({<br> name = optional(string, null)<br> region = optional(string, null)<br> prefix = optional(string, "fleet")<br> retention = optional(number, 5)<br> }), {<br> name = null<br> region = null<br> prefix = "fleet"<br> retention = 5<br> })<br> loadbalancer = object({<br> arn = string<br> })<br> networking = object({<br> subnets = list(string)<br> security_groups = optional(list(string), null)<br> })<br> autoscaling = optional(object({<br> max_capacity = optional(number, 5)<br> min_capacity = optional(number, 1)<br> memory_tracking_target_value = optional(number, 80)<br> cpu_tracking_target_value = optional(number, 80)<br> }), {<br> max_capacity = 5<br> min_capacity = 1<br> memory_tracking_target_value = 80<br> cpu_tracking_target_value = 80<br> })<br> })</pre> | <pre>{<br> "autoscaling": {<br> "cpu_tracking_target_value": 80,<br> "max_capacity": 5,<br> "memory_tracking_target_value": 80,<br> "min_capacity": 1<br> },<br> "awslogs": {<br> "name": null,<br> "prefix": "fleet",<br> "region": null,<br> "retention": 5<br> },<br> "cpu": 256,<br> "database": {<br> "address": null,<br> "database": null,<br> "password_secret_arn": null,<br> "rr_address": null,<br> "user": null<br> },<br> "extra_environment_variables": {},<br> "extra_secrets": {},<br> "iam_role_arn": null,<br> "image": "fleetdm/fleet:v4.22.1",<br> "loadbalancer": {<br> "arn": null<br> },<br> "mem": 512,<br> "networking": {<br> "security_groups": null,<br> "subnets": null<br> },<br> "redis": {<br> "address": null,<br> "use_tls": true<br> },<br> "security_groups": null<br>}</pre> | no |
| <a name="input_migration_config"></a> [migration\_config](#input\_migration\_config) | The configuration object for Fleet's migration task. | <pre>object({<br> mem = number<br> cpu = number<br> })</pre> | <pre>{<br> "cpu": 1024,<br> "mem": 2048<br>}</pre> | no |
| <a name="input_vpc_id"></a> [vpc\_id](#input\_vpc\_id) | n/a | `string` | `null` | no |
## Outputs
| Name | Description |
|------|-------------|
| <a name="output_security_groups"></a> [security\_groups](#output\_security\_groups) | n/a |

View file

@ -0,0 +1,70 @@
data "aws_iam_policy_document" "fleet" {
statement {
effect = "Allow"
actions = ["cloudwatch:PutMetricData"]
resources = ["*"]
}
}
data "aws_iam_policy_document" "assume_role" {
statement {
effect = "Allow"
actions = ["sts:AssumeRole"]
principals {
identifiers = ["ecs.amazonaws.com", "ecs-tasks.amazonaws.com"]
type = "Service"
}
}
}
data "aws_iam_policy_document" "fleet-execution" {
// allow fleet application to obtain the database password from secrets manager
statement {
effect = "Allow"
actions = ["secretsmanager:GetSecretValue"]
resources = [var.fleet_config.database.password_secret_arn]
}
}
resource "aws_iam_role" "main" {
count = var.fleet_config.iam_role_arn == null ? 1 : 0
name = "fleetdm-role"
description = "IAM role that Fleet application assumes when running in ECS"
assume_role_policy = data.aws_iam_policy_document.assume_role.json
}
resource "aws_iam_policy" "main" {
count = var.fleet_config.iam_role_arn == null ? 1 : 0
name = "fleet-iam-policy"
description = "IAM policy that Fleet application uses to define access to AWS resources"
policy = data.aws_iam_policy_document.fleet.json
}
resource "aws_iam_role_policy_attachment" "main" {
count = var.fleet_config.iam_role_arn == null ? 1 : 0
policy_arn = aws_iam_policy.main[0].arn
role = aws_iam_role.main[0].name
}
resource "aws_iam_policy" "execution" {
name = "fleet-iam-policy-execution"
description = "IAM policy that Fleet application uses to define access to AWS resources"
policy = data.aws_iam_policy_document.fleet-execution.json
}
resource "aws_iam_role_policy_attachment" "execution" {
policy_arn = aws_iam_policy.execution.arn
role = aws_iam_role.execution.name
}
resource "aws_iam_role" "execution" {
name = "fleetdm-execution-role"
description = "The execution role for Fleet in ECS"
assume_role_policy = data.aws_iam_policy_document.assume_role.json
}
resource "aws_iam_role_policy_attachment" "role_attachment" {
policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy"
role = aws_iam_role.execution.name
}

View file

@ -0,0 +1,183 @@
data "aws_region" "current" {}
resource "aws_ecs_service" "fleet" {
name = "fleet"
launch_type = "FARGATE"
cluster = var.ecs_cluster
task_definition = aws_ecs_task_definition.backend.arn
desired_count = 1
deployment_minimum_healthy_percent = 100
deployment_maximum_percent = 200
health_check_grace_period_seconds = 30
load_balancer {
target_group_arn = var.fleet_config.loadbalancer.arn
container_name = "fleet"
container_port = 8080
}
lifecycle {
ignore_changes = [desired_count]
}
network_configuration {
subnets = var.fleet_config.networking.subnets
security_groups = var.fleet_config.networking.security_groups == null ? aws_security_group.main.*.id : var.fleet_config.networking.security_groups
}
}
resource "aws_ecs_task_definition" "backend" {
family = "fleet"
network_mode = "awsvpc"
requires_compatibilities = ["FARGATE"]
task_role_arn = var.fleet_config.iam_role_arn == null ? aws_iam_role.main[0].arn : var.fleet_config.iam_role_arn
execution_role_arn = aws_iam_role.execution.arn
cpu = var.fleet_config.cpu
memory = var.fleet_config.mem
container_definitions = jsonencode(
[
{
name = "fleet"
image = var.fleet_config.image
cpu = var.fleet_config.cpu
memory = var.fleet_config.mem
mountPoints = []
volumesFrom = []
essential = true
portMappings = [
{
# This port is the same that the contained application also uses
containerPort = 8080
protocol = "tcp"
}
]
networkMode = "awsvpc"
logConfiguration = {
logDriver = "awslogs"
options = {
awslogs-group = var.fleet_config.awslogs.name == null ? aws_cloudwatch_log_group.main[0].name : var.fleet_config.awslogs.name
awslogs-region = var.fleet_config.awslogs.name == null ? data.aws_region.current.name : var.fleet_config.awslogs.region
awslogs-stream-prefix = var.fleet_config.awslogs.prefix
}
},
ulimits = [
{
name = "nofile"
softLimit = 999999
hardLimit = 999999
}
],
secrets = [
{
name = "FLEET_MYSQL_PASSWORD"
valueFrom = var.fleet_config.database.password_secret_arn
},
{
name = "FLEET_MYSQL_READ_REPLICA_PASSWORD"
valueFrom = var.fleet_config.database.password_secret_arn
}
]
environment = [
{
name = "FLEET_MYSQL_USERNAME"
value = var.fleet_config.database.user
},
{
name = "FLEET_MYSQL_DATABASE"
value = var.fleet_config.database.database
},
{
name = "FLEET_MYSQL_ADDRESS"
value = var.fleet_config.database.address
},
{
name = "FLEET_MYSQL_READ_REPLICA_USERNAME"
value = var.fleet_config.database.user
}, {
name = "FLEET_MYSQL_READ_REPLICA_DATABASE"
value = var.fleet_config.database.database
},
{
name = "FLEET_MYSQL_READ_REPLICA_ADDRESS"
value = var.fleet_config.database.rr_address == null ? var.fleet_config.database.address : var.fleet_config.database.rr_address
},
{
name = "FLEET_REDIS_ADDRESS"
value = var.fleet_config.redis.address
},
{
name = "FLEET_REDIS_USE_TLS"
value = tostring(var.fleet_config.redis.use_tls)
},
{
name = "FLEET_SERVER_TLS"
value = "false"
},
]
}
])
}
resource "aws_appautoscaling_target" "ecs_target" {
max_capacity = var.fleet_config.autoscaling.max_capacity
min_capacity = var.fleet_config.autoscaling.min_capacity
resource_id = "service/${var.ecs_cluster}/${aws_ecs_service.fleet.name}"
scalable_dimension = "ecs:service:DesiredCount"
service_namespace = "ecs"
}
resource "aws_appautoscaling_policy" "ecs_policy_memory" {
name = "fleet-memory-autoscaling"
policy_type = "TargetTrackingScaling"
resource_id = aws_appautoscaling_target.ecs_target.resource_id
scalable_dimension = aws_appautoscaling_target.ecs_target.scalable_dimension
service_namespace = aws_appautoscaling_target.ecs_target.service_namespace
target_tracking_scaling_policy_configuration {
predefined_metric_specification {
predefined_metric_type = "ECSServiceAverageMemoryUtilization"
}
target_value = var.fleet_config.autoscaling.memory_tracking_target_value
}
}
resource "aws_appautoscaling_policy" "ecs_policy_cpu" {
name = "fleet-cpu-autoscaling"
policy_type = "TargetTrackingScaling"
resource_id = aws_appautoscaling_target.ecs_target.resource_id
scalable_dimension = aws_appautoscaling_target.ecs_target.scalable_dimension
service_namespace = aws_appautoscaling_target.ecs_target.service_namespace
target_tracking_scaling_policy_configuration {
predefined_metric_specification {
predefined_metric_type = "ECSServiceAverageCPUUtilization"
}
target_value = var.fleet_config.autoscaling.cpu_tracking_target_value
}
}
resource "aws_cloudwatch_log_group" "main" { #tfsec:ignore:aws-cloudwatch-log-group-customer-key:exp:2022-07-01
count = var.fleet_config.awslogs.name == null ? 1 : 0
name = "fleetdm"
retention_in_days = var.fleet_config.awslogs.retention
}
resource "aws_security_group" "main" {
count = var.fleet_config.security_groups == null ? 1 : 0
name = "fleet"
vpc_id = var.vpc_id
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
ipv6_cidr_blocks = ["::/0"]
}
ingress {
from_port = 8080
to_port = 8080
protocol = "TCP"
cidr_blocks = ["10.0.0.0/8"]
}
}

View file

@ -0,0 +1,3 @@
output "security_groups" {
value = var.fleet_config.networking.security_groups == null ? aws_security_group.main.*.id : var.fleet_config.networking.security_groups
}

View file

@ -0,0 +1,116 @@
variable "ecs_cluster" {
type = string
description = "The name of the ECS cluster to use"
nullable = false
}
variable "vpc_id" {
type = string
default = null
}
variable "fleet_config" {
type = object({
mem = optional(number, 512)
cpu = optional(number, 256)
image = optional(string, "fleetdm/fleet:v4.22.1")
extra_environment_variables = optional(map(string), {})
extra_secrets = optional(map(string), {})
security_groups = optional(list(string), null)
iam_role_arn = optional(string, null)
database = object({
password_secret_arn = string
user = string
database = string
address = string
rr_address = optional(string, null)
})
redis = object({
address = string
use_tls = optional(bool, true)
})
awslogs = optional(object({
name = optional(string, null)
region = optional(string, null)
prefix = optional(string, "fleet")
retention = optional(number, 5)
}), {
name = null
region = null
prefix = "fleet"
retention = 5
})
loadbalancer = object({
arn = string
})
networking = object({
subnets = list(string)
security_groups = optional(list(string), null)
})
autoscaling = optional(object({
max_capacity = optional(number, 5)
min_capacity = optional(number, 1)
memory_tracking_target_value = optional(number, 80)
cpu_tracking_target_value = optional(number, 80)
}), {
max_capacity = 5
min_capacity = 1
memory_tracking_target_value = 80
cpu_tracking_target_value = 80
})
})
default = {
mem = 512
cpu = 256
image = "fleetdm/fleet:v4.22.1"
extra_environment_variables = {}
extra_secrets = {}
security_groups = null
iam_role_arn = null
database = {
password_secret_arn = null
user = null
database = null
address = null
rr_address = null
}
redis = {
address = null
use_tls = true
}
awslogs = {
name = null
region = null
prefix = "fleet"
retention = 5
}
loadbalancer = {
arn = null
}
networking = {
subnets = null
security_groups = null
}
autoscaling = {
max_capacity = 5
min_capacity = 1
memory_tracking_target_value = 80
cpu_tracking_target_value = 80
}
}
description = "The configuration object for Fleet itself. Fields that default to null will have their respective resources created if not specified."
nullable = false
}
variable "migration_config" {
type = object({
mem = number
cpu = number
})
default = {
mem = 2048
cpu = 1024
}
description = "The configuration object for Fleet's migration task."
nullable = false
}

View file

@ -0,0 +1,92 @@
module "ecs" {
source = "./byo-ecs"
ecs_cluster = module.cluster.cluster_name
fleet_config = merge(var.fleet_config, {
loadbalancer = {
arn = module.alb.target_group_arns[0]
}
})
migration_config = var.migration_config
vpc_id = var.vpc_id
}
module "cluster" {
source = "terraform-aws-modules/ecs/aws"
version = "4.1.2"
autoscaling_capacity_providers = var.ecs_cluster.autoscaling_capacity_providers
cluster_configuration = var.ecs_cluster.cluster_configuration
cluster_name = var.ecs_cluster.cluster_name
cluster_settings = var.ecs_cluster.cluster_settings
create = var.ecs_cluster.create
default_capacity_provider_use_fargate = var.ecs_cluster.default_capacity_provider_use_fargate
fargate_capacity_providers = var.ecs_cluster.fargate_capacity_providers
tags = var.ecs_cluster.tags
}
module "alb" {
source = "terraform-aws-modules/alb/aws"
version = "8.2.1"
name = var.alb_config.name
load_balancer_type = "application"
vpc_id = var.vpc_id
subnets = var.alb_config.subnets
security_groups = concat(var.alb_config.security_groups, [aws_security_group.alb.id])
target_groups = [
{
name_prefix = var.alb_config.name
backend_protocol = "HTTP"
backend_port = 80
target_type = "ip"
health_check = {
path = "/healthz"
matcher = "200"
timeout = 10
interval = 15
healthy_threshold = 5
unhealthy_threshold = 5
}
}
]
https_listeners = [
{
port = 443
protocol = "HTTPS"
certificate_arn = var.alb_config.certificate_arn
target_group_index = 0
}
]
http_tcp_listeners = [
{
port = 80
protocol = "HTTP"
target_group_index = 0
}
]
}
resource "aws_security_group" "alb" {
vpc_id = var.vpc_id
ingress {
description = "TLS from VPC"
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
ipv6_cidr_blocks = ["::/0"]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
ipv6_cidr_blocks = ["::/0"]
}
}

View file

@ -0,0 +1,7 @@
output "byo-ecs" {
value = module.ecs
}
output "alb" {
value = module.alb
}

View file

@ -0,0 +1,165 @@
variable "vpc_id" {
type = string
}
variable "ecs_cluster" {
type = object({
autoscaling_capacity_providers = any
cluster_configuration = any
cluster_name = string
cluster_settings = map(string)
create = bool
default_capacity_provider_use_fargate = bool
fargate_capacity_providers = any
tags = map(string)
})
default = {
autoscaling_capacity_providers = {}
cluster_configuration = {
execute_command_configuration = {
logging = "OVERRIDE"
log_configuration = {
cloud_watch_log_group_name = "/aws/ecs/aws-ec2"
}
}
}
cluster_name = "fleet"
cluster_settings = {
"name" : "containerInsights",
"value" : "enabled",
}
create = true
default_capacity_provider_use_fargate = true
fargate_capacity_providers = {
FARGATE = {
default_capacity_provider_strategy = {
weight = 100
}
}
FARGATE_SPOT = {
default_capacity_provider_strategy = {
weight = 0
}
}
}
tags = {}
}
description = "The config for the terraform-aws-modules/ecs/aws module"
nullable = false
}
variable "fleet_config" {
type = object({
mem = optional(number, 512)
cpu = optional(number, 256)
image = optional(string, "fleetdm/fleet:v4.22.1")
extra_environment_variables = optional(map(string), {})
extra_secrets = optional(map(string), {})
security_groups = optional(list(string), null)
iam_role_arn = optional(string, null)
database = object({
password_secret_arn = string
user = string
database = string
address = string
rr_address = optional(string, null)
})
redis = object({
address = string
use_tls = optional(bool, true)
})
awslogs = optional(object({
name = optional(string, null)
region = optional(string, null)
prefix = optional(string, "fleet")
retention = optional(number, 5)
}), {
name = null
region = null
prefix = "fleet"
retention = 5
})
loadbalancer = object({
arn = string
})
networking = object({
subnets = list(string)
security_groups = optional(list(string), null)
})
autoscaling = optional(object({
max_capacity = optional(number, 5)
min_capacity = optional(number, 1)
memory_tracking_target_value = optional(number, 80)
cpu_tracking_target_value = optional(number, 80)
}), {
max_capacity = 5
min_capacity = 1
memory_tracking_target_value = 80
cpu_tracking_target_value = 80
})
})
default = {
mem = 512
cpu = 256
image = "fleetdm/fleet:v4.22.1"
extra_environment_variables = {}
extra_secrets = {}
security_groups = null
iam_role_arn = null
database = {
password_secret_arn = null
user = null
database = null
address = null
rr_address = null
}
redis = {
address = null
use_tls = true
}
awslogs = {
name = null
region = null
prefix = "fleet"
retention = 5
}
loadbalancer = {
arn = null
}
networking = {
subnets = null
security_groups = null
}
autoscaling = {
max_capacity = 5
min_capacity = 1
memory_tracking_target_value = 80
cpu_tracking_target_value = 80
}
}
description = "The configuration object for Fleet itself. Fields that default to null will have their respective resources created if not specified."
nullable = false
}
variable "migration_config" {
type = object({
mem = number
cpu = number
})
default = {
mem = 2048
cpu = 1024
}
description = "The configuration object for Fleet's migration task."
nullable = false
}
variable "alb_config" {
type = object({
name = optional(string, "fleet")
subnets = list(string)
security_groups = optional(list(string), [])
access_logs = optional(map(string), {})
certificate_arn = string
})
}

123
terraform/byo-vpc/main.tf Normal file
View file

@ -0,0 +1,123 @@
module "byo-db" {
source = "./byo-db"
vpc_id = var.vpc_config.vpc_id
fleet_config = merge(var.fleet_config, {
database = {
address = module.rds.cluster_endpoint
database = "fleet"
user = "fleet"
password_secret_arn = module.secrets-manager-1.secret_arns["database-password"]
}
redis = {
address = "${module.redis.endpoint}:${module.redis.port}"
}
networking = {
subnets = var.vpc_config.networking.subnets
}
})
ecs_cluster = var.ecs_cluster
migration_config = var.migration_config
alb_config = var.alb_config
}
resource "random_password" "rds" {
length = 16
special = true
override_special = "!#$%&*()-_=+[]{}<>:?"
}
module "rds" {
source = "terraform-aws-modules/rds-aurora/aws"
version = "7.6.0"
name = var.rds_config.name
engine = "aurora-mysql"
engine_version = var.rds_config.engine_version
instance_class = var.rds_config.instance_class
instances = {
one = {}
two = {}
}
vpc_id = var.vpc_config.vpc_id
subnets = var.rds_config.subnets
allowed_security_groups = concat(module.byo-db.byo-ecs.security_groups, var.rds_config.allowed_security_groups)
allowed_cidr_blocks = var.rds_config.allowed_cidr_blocks
storage_encrypted = true
apply_immediately = var.rds_config.apply_immediately
monitoring_interval = var.rds_config.monitoring_interval
db_parameter_group_name = var.rds_config.db_parameter_group_name == null ? aws_db_parameter_group.main[0].id : var.rds_config.db_parameter_group_name
db_cluster_parameter_group_name = var.rds_config.db_cluster_parameter_group_name == null ? aws_rds_cluster_parameter_group.main[0].id : var.rds_config.db_cluster_parameter_group_name
enabled_cloudwatch_logs_exports = var.rds_config.enabled_cloudwatch_logs_exports
master_username = var.rds_config.master_username
master_password = random_password.rds.result
database_name = "fleet"
skip_final_snapshot = true
}
data "aws_subnet" "redis" {
for_each = toset(var.redis_config.subnets)
id = each.value
}
module "redis" {
source = "cloudposse/elasticache-redis/aws"
version = "0.48.0"
name = var.redis_config.name
replication_group_id = var.redis_config.replication_group_id == null ? var.redis_config.name : var.redis_config.replication_group_id
elasticache_subnet_group_name = var.redis_config.elasticache_subnet_group_name == null ? var.redis_config.name : var.redis_config.elasticache_subnet_group_name
availability_zones = var.redis_config.availability_zones
vpc_id = var.vpc_config.vpc_id
description = "lsjdfldjlfjds"
#allowed_security_group_ids = concat(var.redis_config.allowed_security_group_ids, module.byo-db.ecs.security_group)
subnets = var.redis_config.subnets
cluster_size = var.redis_config.cluster_size
instance_type = var.redis_config.instance_type
apply_immediately = var.redis_config.apply_immediately
automatic_failover_enabled = var.redis_config.automatic_failover_enabled
engine_version = var.redis_config.engine_version
family = var.redis_config.family
at_rest_encryption_enabled = var.redis_config.at_rest_encryption_enabled
transit_encryption_enabled = var.redis_config.transit_encryption_enabled
parameter = var.redis_config.parameter
additional_security_group_rules = [{
type = "ingress"
from_port = 0
to_port = 65535
protocol = "tcp"
cidr_blocks = ["10.0.0.0/8"]
}]
}
module "secrets-manager-1" {
source = "lgallard/secrets-manager/aws"
version = "0.6.1"
secrets = {
database-password = {
description = "fleet-database-password"
recovery_window_in_days = 0
secret_string = module.rds.cluster_master_password
},
}
}
resource "aws_db_parameter_group" "main" {
count = var.rds_config.db_parameter_group_name == null ? 1 : 0
name = "fleet"
family = "aurora-mysql8.0"
description = "fleet"
}
resource "aws_rds_cluster_parameter_group" "main" {
count = var.rds_config.db_cluster_parameter_group_name == null ? 1 : 0
name = "fleet"
family = "aurora-mysql8.0"
description = "fleet"
}

View file

@ -0,0 +1,3 @@
output "byo-db" {
value = module.byo-db
}

View file

@ -0,0 +1,243 @@
variable "vpc_config" {
type = object({
vpc_id = string
networking = object({
subnets = list(string)
})
})
}
variable "rds_config" {
type = object({
name = optional(string, "fleet")
engine_version = optional(string, "8.0.mysql_aurora.3.02.2")
instance_class = optional(string, "db.t4g.large")
subnets = optional(list(string), [])
allowed_security_groups = optional(list(string), [])
allowed_cidr_blocks = optional(list(string), [])
apply_immediately = optional(bool, true)
monitoring_interval = optional(number, 10)
db_parameter_group_name = optional(string)
db_cluster_parameter_group_name = optional(string)
enabled_cloudwatch_logs_exports = optional(list(string), [])
master_username = optional(string, "fleet")
})
default = {
name = "fleet"
engine_version = "8.0.mysql_aurora.3.02.2"
instance_class = "db.t4g.large"
subnets = []
allowed_security_groups = []
allowed_cidr_blocks = []
apply_immediately = true
monitoring_interval = 10
db_parameter_group_name = null
db_cluster_parameter_group_name = null
enabled_cloudwatch_logs_exports = []
master_username = "fleet"
}
description = "The config for the terraform-aws-modules/rds-aurora/aws module"
nullable = false
}
variable "redis_config" {
type = object({
name = optional(string, "fleet")
replication_group_id = optional(string)
elasticache_subnet_group_name = optional(string)
allowed_security_group_ids = optional(list(string), [])
subnets = list(string)
availability_zones = list(string)
cluster_size = optional(number, 3)
instance_type = optional(string, "cache.m5.large")
apply_immediately = optional(bool, true)
automatic_failover_enabled = optional(bool, false)
engine_version = optional(string, "6.x")
family = optional(string, "redis6.x")
at_rest_encryption_enabled = optional(bool, true)
transit_encryption_enabled = optional(bool, true)
parameter = optional(list(object({
name = string
value = string
})), [])
})
default = {
name = "fleet"
replication_group_id = null
elasticache_subnet_group_name = null
allowed_security_group_ids = []
subnets = null
availability_zones = null
cluster_size = 3
instance_type = "cache.m5.large"
apply_immediately = true
automatic_failover_enabled = false
engine_version = "6.x"
family = "redis6.x"
at_rest_encryption_enabled = true
transit_encryption_enabled = true
parameter = []
}
}
variable "ecs_cluster" {
type = object({
autoscaling_capacity_providers = any
cluster_configuration = any
cluster_name = string
cluster_settings = map(string)
create = bool
default_capacity_provider_use_fargate = bool
fargate_capacity_providers = any
tags = map(string)
})
default = {
autoscaling_capacity_providers = {}
cluster_configuration = {
execute_command_configuration = {
logging = "OVERRIDE"
log_configuration = {
cloud_watch_log_group_name = "/aws/ecs/aws-ec2"
}
}
}
cluster_name = "fleet"
cluster_settings = {
"name" : "containerInsights",
"value" : "enabled",
}
create = true
default_capacity_provider_use_fargate = true
fargate_capacity_providers = {
FARGATE = {
default_capacity_provider_strategy = {
weight = 100
}
}
FARGATE_SPOT = {
default_capacity_provider_strategy = {
weight = 0
}
}
}
tags = {}
}
description = "The config for the terraform-aws-modules/ecs/aws module"
nullable = false
}
variable "fleet_config" {
type = object({
mem = optional(number, 512)
cpu = optional(number, 256)
image = optional(string, "fleetdm/fleet:v4.22.1")
extra_environment_variables = optional(map(string), {})
extra_secrets = optional(map(string), {})
security_groups = optional(list(string), null)
iam_role_arn = optional(string, null)
database = object({
password_secret_arn = string
user = string
database = string
address = string
rr_address = optional(string, null)
})
redis = object({
address = string
use_tls = optional(bool, true)
})
awslogs = optional(object({
name = optional(string, null)
region = optional(string, null)
prefix = optional(string, "fleet")
retention = optional(number, 5)
}), {
name = null
region = null
prefix = "fleet"
retention = 5
})
loadbalancer = object({
arn = string
})
networking = object({
subnets = list(string)
security_groups = optional(list(string), null)
})
autoscaling = optional(object({
max_capacity = optional(number, 5)
min_capacity = optional(number, 1)
memory_tracking_target_value = optional(number, 80)
cpu_tracking_target_value = optional(number, 80)
}), {
max_capacity = 5
min_capacity = 1
memory_tracking_target_value = 80
cpu_tracking_target_value = 80
})
})
default = {
mem = 512
cpu = 256
image = "fleetdm/fleet:v4.22.1"
extra_environment_variables = {}
extra_secrets = {}
security_groups = null
iam_role_arn = null
database = {
password_secret_arn = null
user = null
database = null
address = null
rr_address = null
}
redis = {
address = null
use_tls = true
}
awslogs = {
name = null
region = null
prefix = "fleet"
retention = 5
}
loadbalancer = {
arn = null
}
networking = {
subnets = null
security_groups = null
}
autoscaling = {
max_capacity = 5
min_capacity = 1
memory_tracking_target_value = 80
cpu_tracking_target_value = 80
}
}
description = "The configuration object for Fleet itself. Fields that default to null will have their respective resources created if not specified."
nullable = false
}
variable "migration_config" {
type = object({
mem = number
cpu = number
})
default = {
mem = 2048
cpu = 1024
}
description = "The configuration object for Fleet's migration task."
nullable = false
}
variable "alb_config" {
type = object({
name = optional(string, "fleet")
subnets = list(string)
security_groups = optional(list(string), [])
access_logs = optional(map(string), {})
certificate_arn = string
})
}

31
terraform/example/main.tf Normal file
View file

@ -0,0 +1,31 @@
module "main" {
source = "../"
certificate_arn = module.acm.acm_certificate_arn
}
module "acm" {
source = "terraform-aws-modules/acm/aws"
version = "4.3.1"
domain_name = "fleet.loadtest.fleetdm.com"
zone_id = data.aws_route53_zone.main.id
wait_for_validation = true
}
resource "aws_route53_record" "main" {
zone_id = data.aws_route53_zone.main.id
name = "fleet.loadtest.fleetdm.com"
type = "A"
alias {
name = module.main.byo-vpc.byo-db.alb.lb_dns_name
zone_id = module.main.byo-vpc.byo-db.alb.lb_zone_id
evaluate_target_health = true
}
}
data "aws_route53_zone" "main" {
name = "loadtest.fleetdm.com."
private_zone = false
}

41
terraform/main.tf Normal file
View file

@ -0,0 +1,41 @@
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
name = var.vpc.name
cidr = var.vpc.cidr
azs = var.vpc.azs
private_subnets = var.vpc.private_subnets
public_subnets = var.vpc.public_subnets
database_subnets = var.vpc.database_subnets
elasticache_subnets = var.vpc.elasticache_subnets
create_database_subnet_group = var.vpc.create_database_subnet_group
create_database_subnet_route_table = var.vpc.create_database_subnet_route_table
create_elasticache_subnet_group = var.vpc.create_elasticache_subnet_group
create_elasticache_subnet_route_table = var.vpc.create_elasticache_subnet_route_table
enable_vpn_gateway = var.vpc.enable_vpn_gateway
one_nat_gateway_per_az = var.vpc.one_nat_gateway_per_az
single_nat_gateway = var.vpc.single_nat_gateway
enable_nat_gateway = var.vpc.enable_nat_gateway
}
module "byo-vpc" {
source = "./byo-vpc"
vpc_config = {
vpc_id = module.vpc.vpc_id
networking = {
subnets = module.vpc.private_subnets
}
}
rds_config = merge(var.rds_config, {
subnets = module.vpc.database_subnets
})
redis_config = merge(var.redis_config, {
subnets = module.vpc.elasticache_subnets
availability_zones = var.vpc.azs
})
alb_config = merge(var.alb_config, {
subnets = module.vpc.public_subnets
certificate_arn = var.certificate_arn
})
}

3
terraform/outputs.tf Normal file
View file

@ -0,0 +1,3 @@
output "byo-vpc" {
value = module.byo-vpc
}

276
terraform/variables.tf Normal file
View file

@ -0,0 +1,276 @@
variable "vpc" {
type = object({
name = string
cidr = string
azs = list(string)
private_subnets = list(string)
public_subnets = list(string)
database_subnets = list(string)
elasticache_subnets = list(string)
create_database_subnet_group = bool
create_database_subnet_route_table = bool
create_elasticache_subnet_group = bool
create_elasticache_subnet_route_table = bool
enable_vpn_gateway = bool
one_nat_gateway_per_az = bool
single_nat_gateway = bool
enable_nat_gateway = bool
})
default = {
name = "fleet"
cidr = "10.10.0.0/16"
azs = ["us-east-2a", "us-east-2b", "us-east-2c"]
private_subnets = ["10.10.1.0/24", "10.10.2.0/24", "10.10.3.0/24"]
public_subnets = ["10.10.11.0/24", "10.10.12.0/24", "10.10.13.0/24"]
database_subnets = ["10.10.21.0/24", "10.10.22.0/24", "10.10.23.0/24"]
elasticache_subnets = ["10.10.31.0/24", "10.10.32.0/24", "10.10.33.0/24"]
create_database_subnet_group = true
create_database_subnet_route_table = true
create_elasticache_subnet_group = true
create_elasticache_subnet_route_table = true
enable_vpn_gateway = false
one_nat_gateway_per_az = false
single_nat_gateway = true
enable_nat_gateway = true
}
}
variable "certificate_arn" {
type = string
}
variable "rds_config" {
type = object({
name = optional(string, "fleet")
engine_version = optional(string, "8.0.mysql_aurora.3.02.2")
instance_class = optional(string, "db.t4g.large")
subnets = optional(list(string), [])
allowed_security_groups = optional(list(string), [])
allowed_cidr_blocks = optional(list(string), [])
apply_immediately = optional(bool, true)
monitoring_interval = optional(number, 10)
db_parameter_group_name = optional(string)
db_cluster_parameter_group_name = optional(string)
enabled_cloudwatch_logs_exports = optional(list(string), [])
master_username = optional(string, "fleet")
})
default = {
name = "fleet"
engine_version = "8.0.mysql_aurora.3.02.2"
instance_class = "db.t4g.large"
subnets = []
allowed_security_groups = []
allowed_cidr_blocks = []
apply_immediately = true
monitoring_interval = 10
db_parameter_group_name = null
db_cluster_parameter_group_name = null
enabled_cloudwatch_logs_exports = []
master_username = "fleet"
}
description = "The config for the terraform-aws-modules/rds-aurora/aws module"
nullable = false
}
variable "redis_config" {
type = object({
name = optional(string, "fleet")
replication_group_id = optional(string)
elasticache_subnet_group_name = optional(string)
allowed_security_group_ids = optional(list(string), [])
subnets = list(string)
availability_zones = list(string)
cluster_size = optional(number, 3)
instance_type = optional(string, "cache.m5.large")
apply_immediately = optional(bool, true)
automatic_failover_enabled = optional(bool, false)
engine_version = optional(string, "6.x")
family = optional(string, "redis6.x")
at_rest_encryption_enabled = optional(bool, true)
transit_encryption_enabled = optional(bool, true)
parameter = optional(list(object({
name = string
value = string
})), [])
})
default = {
name = "fleet"
replication_group_id = null
elasticache_subnet_group_name = null
allowed_security_group_ids = []
subnets = null
availability_zones = null
cluster_size = 3
instance_type = "cache.m5.large"
apply_immediately = true
automatic_failover_enabled = false
engine_version = "6.x"
family = "redis6.x"
at_rest_encryption_enabled = true
transit_encryption_enabled = true
parameter = []
}
}
variable "ecs_cluster" {
type = object({
autoscaling_capacity_providers = any
cluster_configuration = any
cluster_name = string
cluster_settings = map(string)
create = bool
default_capacity_provider_use_fargate = bool
fargate_capacity_providers = any
tags = map(string)
})
default = {
autoscaling_capacity_providers = {}
cluster_configuration = {
execute_command_configuration = {
logging = "OVERRIDE"
log_configuration = {
cloud_watch_log_group_name = "/aws/ecs/aws-ec2"
}
}
}
cluster_name = "fleet"
cluster_settings = {
"name" : "containerInsights",
"value" : "enabled",
}
create = true
default_capacity_provider_use_fargate = true
fargate_capacity_providers = {
FARGATE = {
default_capacity_provider_strategy = {
weight = 100
}
}
FARGATE_SPOT = {
default_capacity_provider_strategy = {
weight = 0
}
}
}
tags = {}
}
description = "The config for the terraform-aws-modules/ecs/aws module"
nullable = false
}
variable "fleet_config" {
type = object({
mem = optional(number, 512)
cpu = optional(number, 256)
image = optional(string, "fleetdm/fleet:v4.22.1")
extra_environment_variables = optional(map(string), {})
extra_secrets = optional(map(string), {})
security_groups = optional(list(string), null)
iam_role_arn = optional(string, null)
database = object({
password_secret_arn = string
user = string
database = string
address = string
rr_address = optional(string, null)
})
redis = object({
address = string
use_tls = optional(bool, true)
})
awslogs = optional(object({
name = optional(string, null)
region = optional(string, null)
prefix = optional(string, "fleet")
retention = optional(number, 5)
}), {
name = null
region = null
prefix = "fleet"
retention = 5
})
loadbalancer = object({
arn = string
})
networking = object({
subnets = list(string)
security_groups = optional(list(string), null)
})
autoscaling = optional(object({
max_capacity = optional(number, 5)
min_capacity = optional(number, 1)
memory_tracking_target_value = optional(number, 80)
cpu_tracking_target_value = optional(number, 80)
}), {
max_capacity = 5
min_capacity = 1
memory_tracking_target_value = 80
cpu_tracking_target_value = 80
})
})
default = {
mem = 512
cpu = 256
image = "fleetdm/fleet:v4.22.1"
extra_environment_variables = {}
extra_secrets = {}
security_groups = null
iam_role_arn = null
database = {
password_secret_arn = null
user = null
database = null
address = null
rr_address = null
}
redis = {
address = null
use_tls = true
}
awslogs = {
name = null
region = null
prefix = "fleet"
retention = 5
}
loadbalancer = {
arn = null
}
networking = {
subnets = null
security_groups = null
}
autoscaling = {
max_capacity = 5
min_capacity = 1
memory_tracking_target_value = 80
cpu_tracking_target_value = 80
}
}
description = "The configuration object for Fleet itself. Fields that default to null will have their respective resources created if not specified."
nullable = false
}
variable "migration_config" {
type = object({
mem = number
cpu = number
})
description = "The configuration object for Fleet's migration task."
nullable = false
default = {
mem = 2048
cpu = 1024
}
}
variable "alb_config" {
type = object({
name = optional(string, "fleet")
security_groups = optional(list(string), [])
access_logs = optional(map(string), {})
})
default = {}
}