From b87566b50e41f5b6d144793e4f549b49394d52eb Mon Sep 17 00:00:00 2001 From: Xavier Basty Date: Thu, 14 Sep 2023 15:35:46 +0200 Subject: [PATCH] feat: add CloudWatch alarms and alarms forwarding to BetterStack (#131) * feat: add CloudWatch alarms and alarms forwarding to BetterStack * fix: ignore topic encryption warning --- .github/codeowners | 2 + .pre-commit-config.yaml | 2 +- terraform/.terraform-docs.yml | 40 ++++ terraform/README.md | 45 ++--- terraform/cloudwatch/README.md | 43 +++++ terraform/cloudwatch/alarms_docdb.tf | 65 +++++++ terraform/cloudwatch/alarms_ecs.tf | 45 +++++ terraform/cloudwatch/context.tf | 179 ++++++++++++++++++ terraform/cloudwatch/main.tf | 22 +++ terraform/cloudwatch/terraform.tf | 10 + terraform/cloudwatch/variables.tf | 55 ++++++ terraform/docdb/README.md | 61 ++---- terraform/docdb/context.tf | 16 +- terraform/ecs/README.md | 92 +++------ terraform/ecs/cluster_autoscaling.tf | 1 - terraform/ecs/context.tf | 16 +- terraform/ecs/outputs.tf | 10 + terraform/inputs.tf | 29 +++ terraform/main.tf | 32 +--- terraform/monitoring/README.md | 49 ++--- terraform/monitoring/context.tf | 16 +- terraform/monitoring/panels/README.md | 22 +++ .../docdb/low_mem_op_throttled.libsonnet | 2 +- terraform/res_application.tf | 4 - terraform/res_cloudwatch.tf | 12 ++ terraform/res_prometheus.tf | 28 +++ terraform/variables.tf | 11 ++ 27 files changed, 653 insertions(+), 256 deletions(-) create mode 100644 .github/codeowners create mode 100644 terraform/.terraform-docs.yml create mode 100644 terraform/cloudwatch/README.md create mode 100644 terraform/cloudwatch/alarms_docdb.tf create mode 100644 terraform/cloudwatch/alarms_ecs.tf create mode 100644 terraform/cloudwatch/context.tf create mode 100644 terraform/cloudwatch/main.tf create mode 100644 terraform/cloudwatch/terraform.tf create mode 100644 terraform/cloudwatch/variables.tf create mode 100644 terraform/inputs.tf create mode 100644 terraform/monitoring/panels/README.md create mode 100644 terraform/res_cloudwatch.tf create mode 100644 terraform/res_prometheus.tf diff --git a/.github/codeowners b/.github/codeowners new file mode 100644 index 0000000..3af94cb --- /dev/null +++ b/.github/codeowners @@ -0,0 +1,2 @@ +* @Xav +* @Elyniss diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c495836..f50e9a4 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -7,7 +7,7 @@ repos: # - id: terraform_tfsec - id: terraform_docs args: - - '--args=--lockfile=false' + - --args=--config=./terraform/.terraform-docs.yml - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.4.0 diff --git a/terraform/.terraform-docs.yml b/terraform/.terraform-docs.yml new file mode 100644 index 0000000..f112e8b --- /dev/null +++ b/terraform/.terraform-docs.yml @@ -0,0 +1,40 @@ +formatter: 'markdown table' + +recursive: + enabled: true + path: . + +output: + file: README.md + mode: inject + template: |- + + {{ .Content }} + + +content: | + {{ .Header }} + {{ .Requirements }} + {{ .Providers }} + {{ .Modules }} + + ## Inputs + {{- $hideInputs := list "namespace" "region" "stage" "name" "delimiter" "attributes" "tags" "regex_replace_chars" "id_length_limit" "label_key_case" "label_value_case" "label_order" }} + {{- $filteredInputs := list -}} + {{- range .Module.Inputs -}} + {{- if not (has .Name $hideInputs) -}} + {{- $filteredInputs = append $filteredInputs . -}} + {{- end -}} + {{- end -}} + {{ if not $filteredInputs }} + + No inputs. + {{ else }} + | Name | Description | Type | Default | Required | + |------|-------------|------|---------|:--------:| + {{- range $filteredInputs }} + | {{ anchorNameMarkdown "input" .Name }} | {{ tostring .Description | sanitizeMarkdownTbl }} | {{ printf " " }}
{{ tostring .Type | sanitizeMarkdownTbl }}
| {{ printf " " }}
{{ .GetValue | sanitizeMarkdownTbl }}
| {{ printf " " }}{{ ternary .Required "yes" "no" }} | + {{- end }} + {{- end }} + {{ .Outputs }} + {{/** End of file fixer */}} diff --git a/terraform/README.md b/terraform/README.md index cb77f68..c756d96 100644 --- a/terraform/README.md +++ b/terraform/README.md @@ -12,7 +12,8 @@ Now you can apply the changes: `terraform -chdir=terraform apply -var-file="vars/dev.tfvars"` - + + ## Requirements | Name | Version | @@ -21,19 +22,18 @@ Now you can apply the changes: | [aws](#requirement\_aws) | >= 5.7 | | [grafana](#requirement\_grafana) | >= 2.1 | | [random](#requirement\_random) | 3.5.1 | - ## Providers | Name | Version | |------|---------| -| [aws](#provider\_aws) | >= 5.7 | +| [aws](#provider\_aws) | 5.12.0 | | [random](#provider\_random) | 3.5.1 | | [terraform](#provider\_terraform) | n/a | - ## Modules | Name | Source | Version | |------|--------|---------| +| [cloudwatch](#module\_cloudwatch) | ./cloudwatch | n/a | | [dns\_certificate](#module\_dns\_certificate) | app.terraform.io/wallet-connect/dns/aws | 0.1.3 | | [ecs](#module\_ecs) | ./ecs | n/a | | [keystore](#module\_keystore) | ./docdb | n/a | @@ -43,33 +43,22 @@ Now you can apply the changes: | [vpc\_endpoints](#module\_vpc\_endpoints) | terraform-aws-modules/vpc/aws//modules/vpc-endpoints | 5.1 | | [vpc\_flow\_s3\_bucket](#module\_vpc\_flow\_s3\_bucket) | terraform-aws-modules/s3-bucket/aws | ~> 3.14 | -## Resources - -| Name | Type | -|------|------| -| [aws_prometheus_workspace.prometheus](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/prometheus_workspace) | resource | -| [random_pet.this](https://registry.terraform.io/providers/hashicorp/random/3.5.1/docs/resources/pet) | resource | -| [aws_availability_zones.available](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/availability_zones) | data source | -| [aws_iam_policy_document.vpc_flow_log_s3](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | -| [terraform_remote_state.dns](https://registry.terraform.io/providers/hashicorp/terraform/latest/docs/data-sources/remote_state) | data source | -| [terraform_remote_state.monitoring](https://registry.terraform.io/providers/hashicorp/terraform/latest/docs/data-sources/remote_state) | data source | -| [terraform_remote_state.org](https://registry.terraform.io/providers/hashicorp/terraform/latest/docs/data-sources/remote_state) | data source | - ## Inputs - | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| -| [grafana\_auth](#input\_grafana\_auth) | The API Token for the Grafana instance | `string` | `""` | no | -| [image\_version](#input\_image\_version) | The version of the image to deploy | `string` | n/a | yes | -| [keystore\_primary\_instance\_class](#input\_keystore\_primary\_instance\_class) | The instance class of the primary docdb instances | `string` | n/a | yes | -| [keystore\_primary\_instance\_count](#input\_keystore\_primary\_instance\_count) | The number of primary docdb instances to deploy | `number` | n/a | yes | -| [keystore\_replica\_instance\_class](#input\_keystore\_replica\_instance\_class) | The instance class of the replica docdb instances | `string` | n/a | yes | -| [keystore\_replica\_instance\_count](#input\_keystore\_replica\_instance\_count) | The number of replica docdb instances to deploy | `number` | n/a | yes | -| [name](#input\_name) | The name of the application | `string` | `"keyserver"` | no | -| [notification\_channels](#input\_notification\_channels) | The notification channels to send alerts to | `list(any)` | `[]` | no | -| [region](#input\_region) | AWS region to deploy to | `string` | n/a | yes | - +| [betterstack\_cloudwatch\_webhook](#input\_betterstack\_cloudwatch\_webhook) | The BetterStack webhook to send CloudWatch alerts to |
string
|
n/a
| yes | +| [betterstack\_prometheus\_webhook](#input\_betterstack\_prometheus\_webhook) | The BetterStack webhook to send Prometheus alerts to |
string
|
n/a
| yes | +| [grafana\_auth](#input\_grafana\_auth) | The API Token for the Grafana instance |
string
|
""
| no | +| [image\_version](#input\_image\_version) | The version of the image to deploy |
string
|
n/a
| yes | +| [keystore\_primary\_instance\_class](#input\_keystore\_primary\_instance\_class) | The instance class of the primary docdb instances |
string
|
n/a
| yes | +| [keystore\_primary\_instance\_count](#input\_keystore\_primary\_instance\_count) | The number of primary docdb instances to deploy |
number
|
n/a
| yes | +| [keystore\_replica\_instance\_class](#input\_keystore\_replica\_instance\_class) | The instance class of the replica docdb instances |
string
|
n/a
| yes | +| [keystore\_replica\_instance\_count](#input\_keystore\_replica\_instance\_count) | The number of replica docdb instances to deploy |
number
|
n/a
| yes | +| [log\_level](#input\_log\_level) | Defines logging level for the application |
string
|
n/a
| yes | +| [notification\_channels](#input\_notification\_channels) | The notification channels to send alerts to |
list(any)
|
[]
| no | ## Outputs No outputs. - + + + diff --git a/terraform/cloudwatch/README.md b/terraform/cloudwatch/README.md new file mode 100644 index 0000000..27e2df2 --- /dev/null +++ b/terraform/cloudwatch/README.md @@ -0,0 +1,43 @@ +# `cloudwatch` module + +This module configures the cloudwatch alarms and webhook forwarding. + + + +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | ~> 1.0 | +| [aws](#requirement\_aws) | ~> 5.7 | +## Providers + +| Name | Version | +|------|---------| +| [aws](#provider\_aws) | ~> 5.7 | +## Modules + +| Name | Source | Version | +|------|--------|---------| +| [cloudwatch](#module\_cloudwatch) | app.terraform.io/wallet-connect/cloudwatch-constants/aws | 1.0.0 | +| [this](#module\_this) | app.terraform.io/wallet-connect/label/null | 0.3.2 | + +## Inputs +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [context](#input\_context) | Single object for setting entire context at once.
See description of individual variables for details.
Leave string and numeric variables as `null` to use default value.
Individual variable settings (non-null) override settings in context object,
except for attributes and tags, which are merged. |
any
|
n/a
| yes | +| [docdb\_cluster\_id](#input\_docdb\_cluster\_id) | The DocumentDB cluster ID |
string
|
n/a
| yes | +| [docdb\_cpu\_threshold](#input\_docdb\_cpu\_threshold) | The DocumentDB CPU utilization alarm threshold in percents |
number
|
80
| no | +| [docdb\_low\_memory\_throttling\_threshold](#input\_docdb\_low\_memory\_throttling\_threshold) | The DocumentDB low memory throttling alarm threshold in number of operations per period |
number
|
2
| no | +| [docdb\_memory\_threshold](#input\_docdb\_memory\_threshold) | The DocumentDB available memory alarm threshold in GiB |
number
|
4
| no | +| [ecs\_cluster\_name](#input\_ecs\_cluster\_name) | The name of the ECS cluster running the application |
string
|
n/a
| yes | +| [ecs\_cpu\_threshold](#input\_ecs\_cpu\_threshold) | The ECS CPU utilization alarm threshold in percents |
number
|
80
| no | +| [ecs\_memory\_threshold](#input\_ecs\_memory\_threshold) | The ECS memory utilization alarm threshold in percents |
number
|
80
| no | +| [ecs\_service\_name](#input\_ecs\_service\_name) | The name of the ECS service running the application |
string
|
n/a
| yes | +| [webhook\_url](#input\_webhook\_url) | The URL of the webhook to be called on alarms |
string
|
n/a
| yes | +## Outputs + +No outputs. + + + diff --git a/terraform/cloudwatch/alarms_docdb.tf b/terraform/cloudwatch/alarms_docdb.tf new file mode 100644 index 0000000..a1a411c --- /dev/null +++ b/terraform/cloudwatch/alarms_docdb.tf @@ -0,0 +1,65 @@ +resource "aws_cloudwatch_metric_alarm" "docdb_cpu_utilization" { + alarm_name = "${local.alarm_prefix} - DocumentDB CPU Utilization" + alarm_description = "${local.alarm_prefix} - DocumentDB CPU utilization is high (over ${var.docdb_cpu_threshold}%)" + + namespace = module.cloudwatch.namespaces.DocumentDB + dimensions = { + DBClusterIdentifier = var.docdb_cluster_id + } + metric_name = module.cloudwatch.metrics.DocumentDB.CPUUtilization + + evaluation_periods = local.evaluation_periods + period = local.period + + statistic = module.cloudwatch.statistics.Average + comparison_operator = module.cloudwatch.operators.GreaterThanOrEqualToThreshold + threshold = var.docdb_cpu_threshold + treat_missing_data = "breaching" + + alarm_actions = [aws_sns_topic.webhook.arn] + insufficient_data_actions = [aws_sns_topic.webhook.arn] +} + +resource "aws_cloudwatch_metric_alarm" "docdb_available_memory" { + alarm_name = "${local.alarm_prefix} - DocumentDB Available Memory" + alarm_description = "${local.alarm_prefix} - DocumentDB available memory is low (less than ${var.docdb_memory_threshold}GiB)" + + namespace = module.cloudwatch.namespaces.DocumentDB + dimensions = { + DBClusterIdentifier = var.docdb_cluster_id + } + metric_name = module.cloudwatch.metrics.DocumentDB.FreeableMemory + + evaluation_periods = local.evaluation_periods + period = local.period + + statistic = module.cloudwatch.statistics.Average + comparison_operator = module.cloudwatch.operators.LessThanOrEqualToThreshold + threshold = var.docdb_memory_threshold * pow(1000, 3) + treat_missing_data = "breaching" + + alarm_actions = [aws_sns_topic.webhook.arn] + insufficient_data_actions = [aws_sns_topic.webhook.arn] +} + +resource "aws_cloudwatch_metric_alarm" "docdb_low_memory_throttling" { + alarm_name = "${local.alarm_prefix} - DocumentDB Low Memory Throttling" + alarm_description = "${local.alarm_prefix} - DocumentDB is throttling operations due to low memory" + + namespace = module.cloudwatch.namespaces.DocumentDB + dimensions = { + DBClusterIdentifier = var.docdb_cluster_id + } + metric_name = module.cloudwatch.metrics.DocumentDB.LowMemNumOperationsThrottled + + evaluation_periods = local.evaluation_periods + period = local.period + + statistic = module.cloudwatch.statistics.Maximum + comparison_operator = module.cloudwatch.operators.GreaterThanThreshold + threshold = var.docdb_low_memory_throttling_threshold + treat_missing_data = "breaching" + + alarm_actions = [aws_sns_topic.webhook.arn] + insufficient_data_actions = [aws_sns_topic.webhook.arn] +} diff --git a/terraform/cloudwatch/alarms_ecs.tf b/terraform/cloudwatch/alarms_ecs.tf new file mode 100644 index 0000000..fae4065 --- /dev/null +++ b/terraform/cloudwatch/alarms_ecs.tf @@ -0,0 +1,45 @@ +resource "aws_cloudwatch_metric_alarm" "ecs_cpu_utilization" { + alarm_name = "${local.alarm_prefix} - ECS CPU Utilization" + alarm_description = "${local.alarm_prefix} - ECS CPU utilization is high (over ${var.ecs_cpu_threshold}%)" + + namespace = module.cloudwatch.namespaces.ECS + dimensions = { + ClusterName = var.ecs_cluster_name + ServiceName = var.ecs_service_name + } + metric_name = module.cloudwatch.metrics.ECS.CPUUtilization + + evaluation_periods = local.evaluation_periods + period = local.period + + statistic = module.cloudwatch.statistics.Average + comparison_operator = module.cloudwatch.operators.GreaterThanOrEqualToThreshold + threshold = var.ecs_cpu_threshold + treat_missing_data = "breaching" + + alarm_actions = [aws_sns_topic.webhook.arn] + insufficient_data_actions = [aws_sns_topic.webhook.arn] +} + +resource "aws_cloudwatch_metric_alarm" "ecs_mem_utilization" { + alarm_name = "${local.alarm_prefix} - ECS Memory Utilization" + alarm_description = "${local.alarm_prefix} - ECS Memory utilization is high (over ${var.ecs_memory_threshold}%)" + + namespace = module.cloudwatch.namespaces.ECS + dimensions = { + ClusterName = var.ecs_cluster_name + ServiceName = var.ecs_service_name + } + metric_name = module.cloudwatch.metrics.ECS.MemoryUtilization + + evaluation_periods = local.evaluation_periods + period = local.period + + statistic = module.cloudwatch.statistics.Average + comparison_operator = module.cloudwatch.operators.GreaterThanOrEqualToThreshold + threshold = var.ecs_memory_threshold + treat_missing_data = "breaching" + + alarm_actions = [aws_sns_topic.webhook.arn] + insufficient_data_actions = [aws_sns_topic.webhook.arn] +} diff --git a/terraform/cloudwatch/context.tf b/terraform/cloudwatch/context.tf new file mode 100644 index 0000000..da1c290 --- /dev/null +++ b/terraform/cloudwatch/context.tf @@ -0,0 +1,179 @@ +module "this" { + source = "app.terraform.io/wallet-connect/label/null" + version = "0.3.2" + + namespace = var.namespace + region = var.region + stage = var.stage + name = var.name + delimiter = var.delimiter + attributes = var.attributes + tags = var.tags + label_order = var.label_order + regex_replace_chars = var.regex_replace_chars + id_length_limit = var.id_length_limit + label_key_case = var.label_key_case + label_value_case = var.label_value_case + + context = var.context +} + +################################################################################ +# Copy contents of label/variables.tf here + +#tflint-ignore: terraform_standard_module_structure +variable "context" { + type = any + description = <<-EOT + Single object for setting entire context at once. + See description of individual variables for details. + Leave string and numeric variables as `null` to use default value. + Individual variable settings (non-null) override settings in context object, + except for attributes and tags, which are merged. + EOT + + validation { + condition = lookup(var.context, "label_key_case", null) == null ? true : contains(["lower", "title", "upper"], var.context["label_key_case"]) + error_message = "Allowed values: `lower`, `title`, `upper`." + } + + validation { + condition = lookup(var.context, "label_value_case", null) == null ? true : contains(["lower", "title", "upper", "none"], var.context["label_value_case"]) + error_message = "Allowed values: `lower`, `title`, `upper`, `none`." + } +} + +#tflint-ignore: terraform_standard_module_structure +variable "namespace" { + type = string + default = null + description = "ID element. Usually the organization name, i.e. 'walletconnect' to help ensure generated IDs are globally unique." +} + +#tflint-ignore: terraform_standard_module_structure +variable "region" { + type = string + default = null + description = "ID element. Usually used for region e.g. 'uw2', 'us-west-2'." +} + +#tflint-ignore: terraform_standard_module_structure +variable "stage" { + type = string + default = null + description = "ID element. Usually used to indicate role, e.g. 'prod', 'staging', 'source', 'build', 'test', 'deploy', 'release'." +} + +#tflint-ignore: terraform_standard_module_structure +variable "name" { + type = string + default = null + description = <<-EOT + ID element. Usually the component name. + This is the only ID element not also included as a `tag`. + The "name" tag is set to the full `id` string. There is no tag with the value of the `name` input. + EOT +} + +#tflint-ignore: terraform_standard_module_structure +variable "delimiter" { + type = string + default = null + description = <<-EOT + Delimiter to be used between ID elements. + Defaults to `-` (hyphen). Set to `""` to use no delimiter at all. + EOT +} + +#tflint-ignore: terraform_standard_module_structure +variable "attributes" { + type = list(string) + default = [] + description = <<-EOT + ID element. Additional attributes (e.g. `workers` or `cluster`) to add to `id`, + in the order they appear in the list. New attributes are appended to the + end of the list. The elements of the list are joined by the `delimiter` + and treated as a single ID element. + EOT +} + +#tflint-ignore: terraform_standard_module_structure +variable "tags" { + type = map(string) + default = {} + description = "Additional tags." +} + +#tflint-ignore: terraform_standard_module_structure +variable "label_order" { + type = list(string) + default = null + description = <<-EOT + The order in which the labels (ID elements) appear in the `id`. + Defaults to ["namespace", "region", "stage", "name", "attributes"]. + You can omit any of the 5 labels, but at least one must be present. + EOT +} + +#tflint-ignore: terraform_standard_module_structure +variable "regex_replace_chars" { + type = string + default = null + description = <<-EOT + Terraform regular expression (regex) string. + Characters matching the regex will be removed from the ID elements. + If not set, `"/[^a-zA-Z0-9-]/"` is used to remove all characters other than hyphens, letters and digits. + EOT +} + +#tflint-ignore: terraform_standard_module_structure +variable "id_length_limit" { + type = number + default = null + description = <<-EOT + Limit `id` to this many characters (minimum 6). + Set to `0` for unlimited length. + Set to `null` for keep the existing setting, which defaults to `0`. + Does not affect `id_full`. + EOT + validation { + condition = var.id_length_limit == null ? true : var.id_length_limit >= 6 || var.id_length_limit == 0 + error_message = "The id_length_limit must be >= 6 if supplied (not null), or 0 for unlimited length." + } +} + +#tflint-ignore: terraform_standard_module_structure +variable "label_key_case" { + type = string + default = null + description = <<-EOT + Controls the letter case of the `tags` keys (label names) for tags generated by this module. + Does not affect keys of tags passed in via the `tags` input. + Possible values: `lower`, `title`, `upper`. + Default value: `title`. + EOT + + validation { + condition = var.label_key_case == null ? true : contains(["lower", "title", "upper"], var.label_key_case) + error_message = "Allowed values: `lower`, `title`, `upper`." + } +} + +#tflint-ignore: terraform_standard_module_structure +variable "label_value_case" { + type = string + default = null + description = <<-EOT + Controls the letter case of ID elements (labels) as included in `id`, + set as tag values, and output by this module individually. + Does not affect values of tags passed in via the `tags` input. + Possible values: `lower`, `title`, `upper` and `none` (no transformation). + Set this to `title` and set `delimiter` to `""` to yield Pascal Case IDs. + Default value: `lower`. + EOT + + validation { + condition = var.label_value_case == null ? true : contains(["lower", "title", "upper", "none"], var.label_value_case) + error_message = "Allowed values: `lower`, `title`, `upper`, `none`." + } +} diff --git a/terraform/cloudwatch/main.tf b/terraform/cloudwatch/main.tf new file mode 100644 index 0000000..7722495 --- /dev/null +++ b/terraform/cloudwatch/main.tf @@ -0,0 +1,22 @@ +module "cloudwatch" { + source = "app.terraform.io/wallet-connect/cloudwatch-constants/aws" + version = "1.0.0" +} + +locals { + alarm_prefix = "${title(module.this.name)} - ${title(module.this.stage)}" + evaluation_periods = 1 + period = 60 * 5 +} + +#tfsec:ignore:aws-sns-enable-topic-encryption +resource "aws_sns_topic" "webhook" { + name = "cloudwatch-webhook" + display_name = "CloudWatch Webhook forwarding to BetterUptime" +} + +resource "aws_sns_topic_subscription" "webhook" { + endpoint = var.webhook_url + protocol = "https" + topic_arn = aws_sns_topic.webhook.arn +} diff --git a/terraform/cloudwatch/terraform.tf b/terraform/cloudwatch/terraform.tf new file mode 100644 index 0000000..f4c0a25 --- /dev/null +++ b/terraform/cloudwatch/terraform.tf @@ -0,0 +1,10 @@ +terraform { + required_version = "~> 1.0" + + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 5.7" + } + } +} diff --git a/terraform/cloudwatch/variables.tf b/terraform/cloudwatch/variables.tf new file mode 100644 index 0000000..de6d14a --- /dev/null +++ b/terraform/cloudwatch/variables.tf @@ -0,0 +1,55 @@ +variable "webhook_url" { + description = "The URL of the webhook to be called on alarms" + type = string +} + +#------------------------------------------------------------------------------- +# ECS + +variable "ecs_cluster_name" { + description = "The name of the ECS cluster running the application" + type = string +} + +variable "ecs_service_name" { + description = "The name of the ECS service running the application" + type = string +} + +variable "ecs_cpu_threshold" { + description = "The ECS CPU utilization alarm threshold in percents" + type = number + default = 80 +} + +variable "ecs_memory_threshold" { + description = "The ECS memory utilization alarm threshold in percents" + type = number + default = 80 +} + +#------------------------------------------------------------------------------- +# DocumentDB + +variable "docdb_cluster_id" { + description = "The DocumentDB cluster ID" + type = string +} + +variable "docdb_cpu_threshold" { + description = "The DocumentDB CPU utilization alarm threshold in percents" + type = number + default = 80 +} + +variable "docdb_memory_threshold" { + description = "The DocumentDB available memory alarm threshold in GiB" + type = number + default = 4 +} + +variable "docdb_low_memory_throttling_threshold" { + description = "The DocumentDB low memory throttling alarm threshold in number of operations per period" + type = number + default = 2 +} diff --git a/terraform/docdb/README.md b/terraform/docdb/README.md index 2fc389d..a62fa72 100644 --- a/terraform/docdb/README.md +++ b/terraform/docdb/README.md @@ -2,7 +2,8 @@ Creates a DocumentDB cluster with auto-scaled read replicas. - + + ## Requirements | Name | Version | @@ -10,63 +11,33 @@ Creates a DocumentDB cluster with auto-scaled read replicas. | [terraform](#requirement\_terraform) | ~> 1.0 | | [aws](#requirement\_aws) | ~> 5.7 | | [random](#requirement\_random) | ~> 3.5 | - ## Providers | Name | Version | |------|---------| | [aws](#provider\_aws) | ~> 5.7 | | [random](#provider\_random) | ~> 3.5 | - ## Modules | Name | Source | Version | |------|--------|---------| | [this](#module\_this) | app.terraform.io/wallet-connect/label/null | 0.3.2 | -## Resources - -| Name | Type | -|------|------| -| [aws_docdb_cluster.main](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/docdb_cluster) | resource | -| [aws_docdb_cluster_instance.primary](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/docdb_cluster_instance) | resource | -| [aws_docdb_cluster_instance.replica](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/docdb_cluster_instance) | resource | -| [aws_docdb_subnet_group.private_subnets](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/docdb_subnet_group) | resource | -| [aws_kms_key.docdb_encryption](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/kms_key) | resource | -| [aws_secretsmanager_secret.master_password](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/secretsmanager_secret) | resource | -| [aws_secretsmanager_secret_version.master_password](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/secretsmanager_secret_version) | resource | -| [aws_security_group.service_security_group](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group) | resource | -| [random_password.master_password](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/password) | resource | - ## Inputs - | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| -| [allowed\_egress\_cidr\_blocks](#input\_allowed\_egress\_cidr\_blocks) | The CIDR blocks to allow egress to | `list(string)` | n/a | yes | -| [allowed\_ingress\_cidr\_blocks](#input\_allowed\_ingress\_cidr\_blocks) | The CIDR blocks to allow ingress from | `list(string)` | n/a | yes | -| [attributes](#input\_attributes) | ID element. Additional attributes (e.g. `workers` or `cluster`) to add to `id`,
in the order they appear in the list. New attributes are appended to the
end of the list. The elements of the list are joined by the `delimiter`
and treated as a single ID element. | `list(string)` | `[]` | no | -| [context](#input\_context) | Single object for setting entire context at once.
See description of individual variables for details.
Leave string and numeric variables as `null` to use default value.
Individual variable settings (non-null) override settings in context object,
except for attributes and tags, which are merged. | `any` |
{
"attributes": [],
"delimiter": null,
"id_length_limit": null,
"label_key_case": null,
"label_order": [],
"label_value_case": null,
"name": null,
"namespace": null,
"regex_replace_chars": null,
"region": null,
"stage": null,
"tags": {}
}
| no | -| [db\_name](#input\_db\_name) | The name of the mongo database | `string` | n/a | yes | -| [default\_database](#input\_default\_database) | The name of the default database to create | `string` | n/a | yes | -| [delimiter](#input\_delimiter) | Delimiter to be used between ID elements.
Defaults to `-` (hyphen). Set to `""` to use no delimiter at all. | `string` | `null` | no | -| [id\_length\_limit](#input\_id\_length\_limit) | Limit `id` to this many characters (minimum 6).
Set to `0` for unlimited length.
Set to `null` for keep the existing setting, which defaults to `0`.
Does not affect `id_full`. | `number` | `null` | no | -| [label\_key\_case](#input\_label\_key\_case) | Controls the letter case of the `tags` keys (label names) for tags generated by this module.
Does not affect keys of tags passed in via the `tags` input.
Possible values: `lower`, `title`, `upper`.
Default value: `title`. | `string` | `null` | no | -| [label\_order](#input\_label\_order) | The order in which the labels (ID elements) appear in the `id`.
Defaults to ["namespace", "region", "stage", "name", "attributes"].
You can omit any of the 5 labels, but at least one must be present. | `list(string)` | `null` | no | -| [label\_value\_case](#input\_label\_value\_case) | Controls the letter case of ID elements (labels) as included in `id`,
set as tag values, and output by this module individually.
Does not affect values of tags passed in via the `tags` input.
Possible values: `lower`, `title`, `upper` and `none` (no transformation).
Set this to `title` and set `delimiter` to `""` to yield Pascal Case IDs.
Default value: `lower`. | `string` | `null` | no | -| [name](#input\_name) | ID element. Usually the component name.
This is the only ID element not also included as a `tag`.
The "name" tag is set to the full `id` string. There is no tag with the value of the `name` input. | `string` | `null` | no | -| [namespace](#input\_namespace) | ID element. Usually the organization name, i.e. 'walletconnect' to help ensure generated IDs are globally unique. | `string` | `null` | no | -| [port](#input\_port) | The port the mongo database will listen on | `number` | `27017` | no | -| [primary\_instance\_class](#input\_primary\_instance\_class) | The instance class of the primary instances | `string` | n/a | yes | -| [primary\_instance\_count](#input\_primary\_instance\_count) | The number of primary instances to create | `number` | n/a | yes | -| [private\_subnet\_ids](#input\_private\_subnet\_ids) | The IDs of the private subnets to deploy to | `list(string)` | n/a | yes | -| [regex\_replace\_chars](#input\_regex\_replace\_chars) | Terraform regular expression (regex) string.
Characters matching the regex will be removed from the ID elements.
If not set, `"/[^a-zA-Z0-9-]/"` is used to remove all characters other than hyphens, letters and digits. | `string` | `null` | no | -| [region](#input\_region) | ID element. Usually used for region e.g. 'uw2', 'us-west-2'. | `string` | `null` | no | -| [replica\_instance\_class](#input\_replica\_instance\_class) | The instance class of the replica instances | `string` | n/a | yes | -| [replica\_instance\_count](#input\_replica\_instance\_count) | The number of replica instances to create | `number` | n/a | yes | -| [stage](#input\_stage) | ID element. Usually used to indicate role, e.g. 'prod', 'staging', 'source', 'build', 'test', 'deploy', 'release'. | `string` | `null` | no | -| [tags](#input\_tags) | Additional tags. | `map(string)` | `{}` | no | -| [vpc\_id](#input\_vpc\_id) | The ID of the VPC to deploy to | `string` | n/a | yes | - +| [allowed\_egress\_cidr\_blocks](#input\_allowed\_egress\_cidr\_blocks) | The CIDR blocks to allow egress to |
list(string)
|
n/a
| yes | +| [allowed\_ingress\_cidr\_blocks](#input\_allowed\_ingress\_cidr\_blocks) | The CIDR blocks to allow ingress from |
list(string)
|
n/a
| yes | +| [context](#input\_context) | Single object for setting entire context at once.
See description of individual variables for details.
Leave string and numeric variables as `null` to use default value.
Individual variable settings (non-null) override settings in context object,
except for attributes and tags, which are merged. |
any
|
n/a
| yes | +| [db\_name](#input\_db\_name) | The name of the mongo database |
string
|
n/a
| yes | +| [default\_database](#input\_default\_database) | The name of the default database to create |
string
|
n/a
| yes | +| [port](#input\_port) | The port the mongo database will listen on |
number
|
27017
| no | +| [primary\_instance\_class](#input\_primary\_instance\_class) | The instance class of the primary instances |
string
|
n/a
| yes | +| [primary\_instance\_count](#input\_primary\_instance\_count) | The number of primary instances to create |
number
|
n/a
| yes | +| [private\_subnet\_ids](#input\_private\_subnet\_ids) | The IDs of the private subnets to deploy to |
list(string)
|
n/a
| yes | +| [replica\_instance\_class](#input\_replica\_instance\_class) | The instance class of the replica instances |
string
|
n/a
| yes | +| [replica\_instance\_count](#input\_replica\_instance\_count) | The number of replica instances to create |
number
|
n/a
| yes | +| [vpc\_id](#input\_vpc\_id) | The ID of the VPC to deploy to |
string
|
n/a
| yes | ## Outputs | Name | Description | @@ -77,4 +48,6 @@ Creates a DocumentDB cluster with auto-scaled read replicas. | [password](#output\_password) | The master password | | [port](#output\_port) | The connection port | | [username](#output\_username) | The master username | - + + + diff --git a/terraform/docdb/context.tf b/terraform/docdb/context.tf index 9871e49..da1c290 100644 --- a/terraform/docdb/context.tf +++ b/terraform/docdb/context.tf @@ -23,21 +23,7 @@ module "this" { #tflint-ignore: terraform_standard_module_structure variable "context" { - type = any - default = { - namespace = null - region = null - stage = null - name = null - delimiter = null - attributes = [] - tags = {} - label_order = [] - regex_replace_chars = null - id_length_limit = null - label_key_case = null - label_value_case = null - } + type = any description = <<-EOT Single object for setting entire context at once. See description of individual variables for details. diff --git a/terraform/ecs/README.md b/terraform/ecs/README.md index ddb16cf..8636cab 100644 --- a/terraform/ecs/README.md +++ b/terraform/ecs/README.md @@ -2,7 +2,8 @@ This module creates an ECS cluster and an autoscaling group of EC2 instances to run the application. - + + ## Requirements | Name | Version | @@ -10,14 +11,12 @@ This module creates an ECS cluster and an autoscaling group of EC2 instances to | [terraform](#requirement\_terraform) | ~> 1.0 | | [aws](#requirement\_aws) | ~> 5.7 | | [random](#requirement\_random) | 3.5.1 | - ## Providers | Name | Version | |------|---------| | [aws](#provider\_aws) | ~> 5.7 | | [random](#provider\_random) | 3.5.1 | - ## Modules | Name | Source | Version | @@ -25,78 +24,39 @@ This module creates an ECS cluster and an autoscaling group of EC2 instances to | [ecs\_cpu\_mem](#module\_ecs\_cpu\_mem) | app.terraform.io/wallet-connect/ecs_cpu_mem/aws | 1.0.0 | | [this](#module\_this) | app.terraform.io/wallet-connect/label/null | 0.3.2 | -## Resources - -| Name | Type | -|------|------| -| [aws_appautoscaling_policy.ecs_target_cpu](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/appautoscaling_policy) | resource | -| [aws_appautoscaling_policy.ecs_target_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.cluster_logs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_group) | resource | -| [aws_ecs_cluster.app_cluster](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecs_cluster) | resource | -| [aws_ecs_service.app_service](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecs_service) | resource | -| [aws_ecs_task_definition.app_task](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecs_task_definition) | resource | -| [aws_iam_policy.otel](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | -| [aws_iam_role.ecs_autoscaling_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | -| [aws_iam_role.ecs_task_execution_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | -| [aws_iam_role_policy_attachment.cloudwatch_write_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | -| [aws_iam_role_policy_attachment.ecs_task_execution_fetch_ghcr_secret_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | -| [aws_iam_role_policy_attachment.ecs_task_execution_role_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | -| [aws_iam_role_policy_attachment.prometheus_write_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | -| [aws_iam_role_policy_attachment.ssm_read_only_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | -| [aws_lb.load_balancer](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lb) | resource | -| [aws_lb_listener.listener-http](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lb_listener) | resource | -| [aws_lb_listener.listener-https](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lb_listener) | resource | -| [aws_lb_listener_certificate.listener-https](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lb_listener_certificate) | resource | -| [aws_lb_target_group.target_group](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lb_target_group) | resource | -| [aws_route53_record.dns_load_balancer](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_record) | resource | -| [aws_security_group.app_ingress](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group) | resource | -| [aws_security_group.lb_ingress](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group) | resource | -| [random_pet.this](https://registry.terraform.io/providers/hashicorp/random/3.5.1/docs/resources/pet) | resource | -| [aws_iam_policy_document.ecs_task_assume_role_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | -| [aws_iam_policy_document.otel](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | - ## Inputs - | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| -| [allowed\_app\_ingress\_cidr\_blocks](#input\_allowed\_app\_ingress\_cidr\_blocks) | A list of CIDR blocks to allow ingress access to the application. | `string` | n/a | yes | -| [allowed\_lb\_ingress\_cidr\_blocks](#input\_allowed\_lb\_ingress\_cidr\_blocks) | A list of CIDR blocks to allow ingress access to the load-balancer. | `string` | n/a | yes | -| [attributes](#input\_attributes) | ID element. Additional attributes (e.g. `workers` or `cluster`) to add to `id`,
in the order they appear in the list. New attributes are appended to the
end of the list. The elements of the list are joined by the `delimiter`
and treated as a single ID element. | `list(string)` | `[]` | no | -| [context](#input\_context) | Single object for setting entire context at once.
See description of individual variables for details.
Leave string and numeric variables as `null` to use default value.
Individual variable settings (non-null) override settings in context object,
except for attributes and tags, which are merged. | `any` |
{
"attributes": [],
"delimiter": null,
"id_length_limit": null,
"label_key_case": null,
"label_order": [],
"label_value_case": null,
"name": null,
"namespace": null,
"regex_replace_chars": null,
"region": null,
"stage": null,
"tags": {}
}
| no | -| [delimiter](#input\_delimiter) | Delimiter to be used between ID elements.
Defaults to `-` (hyphen). Set to `""` to use no delimiter at all. | `string` | `null` | no | -| [ecr\_repository\_url](#input\_ecr\_repository\_url) | The URL of the ECR repository where the app image is stored | `string` | n/a | yes | -| [id\_length\_limit](#input\_id\_length\_limit) | Limit `id` to this many characters (minimum 6).
Set to `0` for unlimited length.
Set to `null` for keep the existing setting, which defaults to `0`.
Does not affect `id_full`. | `number` | `null` | no | -| [image\_version](#input\_image\_version) | The version of the app image to deploy | `string` | n/a | yes | -| [keystore\_addr](#input\_keystore\_addr) | The address of the MongoDB instance to use for the persistent keystore | `string` | n/a | yes | -| [label\_key\_case](#input\_label\_key\_case) | Controls the letter case of the `tags` keys (label names) for tags generated by this module.
Does not affect keys of tags passed in via the `tags` input.
Possible values: `lower`, `title`, `upper`.
Default value: `title`. | `string` | `null` | no | -| [label\_order](#input\_label\_order) | The order in which the labels (ID elements) appear in the `id`.
Defaults to ["namespace", "region", "stage", "name", "attributes"].
You can omit any of the 5 labels, but at least one must be present. | `list(string)` | `null` | no | -| [label\_value\_case](#input\_label\_value\_case) | Controls the letter case of ID elements (labels) as included in `id`,
set as tag values, and output by this module individually.
Does not affect values of tags passed in via the `tags` input.
Possible values: `lower`, `title`, `upper` and `none` (no transformation).
Set this to `title` and set `delimiter` to `""` to yield Pascal Case IDs.
Default value: `lower`. | `string` | `null` | no | -| [max\_capacity](#input\_max\_capacity) | Maximum number of instances in the autoscaling group | `number` | `8` | no | -| [min\_capacity](#input\_min\_capacity) | Minimum number of instances in the autoscaling group | `number` | `2` | no | -| [name](#input\_name) | ID element. Usually the component name.
This is the only ID element not also included as a `tag`.
The "name" tag is set to the full `id` string. There is no tag with the value of the `name` input. | `string` | `null` | no | -| [namespace](#input\_namespace) | ID element. Usually the organization name, i.e. 'walletconnect' to help ensure generated IDs are globally unique. | `string` | `null` | no | -| [port](#input\_port) | The port the app listens on | `number` | n/a | yes | -| [private\_subnets](#input\_private\_subnets) | The IDs of the private subnets to deploy to | `list(string)` | n/a | yes | -| [prometheus\_endpoint](#input\_prometheus\_endpoint) | The endpoint of the Prometheus server to use for monitoring | `string` | n/a | yes | -| [public\_subnets](#input\_public\_subnets) | The IDs of the public subnets to deploy to | `list(string)` | n/a | yes | -| [regex\_replace\_chars](#input\_regex\_replace\_chars) | Terraform regular expression (regex) string.
Characters matching the regex will be removed from the ID elements.
If not set, `"/[^a-zA-Z0-9-]/"` is used to remove all characters other than hyphens, letters and digits. | `string` | `null` | no | -| [region](#input\_region) | ID element. Usually used for region e.g. 'uw2', 'us-west-2'. | `string` | `null` | no | -| [route53\_zones](#input\_route53\_zones) | The FQDNs to use for the app | `map(string)` | n/a | yes | -| [route53\_zones\_certificates](#input\_route53\_zones\_certificates) | The ARNs of the ACM certificates to use for HTTPS | `map(string)` | n/a | yes | -| [stage](#input\_stage) | ID element. Usually used to indicate role, e.g. 'prod', 'staging', 'source', 'build', 'test', 'deploy', 'release'. | `string` | `null` | no | -| [tags](#input\_tags) | Additional tags. | `map(string)` | `{}` | no | -| [task\_cpu](#input\_task\_cpu) | The number of CPU units to reserve for the container. | `number` | n/a | yes | -| [task\_memory](#input\_task\_memory) | The amount of memory (in MiB) to reserve for the container. | `number` | n/a | yes | -| [vpc\_id](#input\_vpc\_id) | The ID of the VPC to deploy to | `string` | n/a | yes | - +| [allowed\_app\_ingress\_cidr\_blocks](#input\_allowed\_app\_ingress\_cidr\_blocks) | A list of CIDR blocks to allow ingress access to the application. |
string
|
n/a
| yes | +| [allowed\_lb\_ingress\_cidr\_blocks](#input\_allowed\_lb\_ingress\_cidr\_blocks) | A list of CIDR blocks to allow ingress access to the load-balancer. |
string
|
n/a
| yes | +| [context](#input\_context) | Single object for setting entire context at once.
See description of individual variables for details.
Leave string and numeric variables as `null` to use default value.
Individual variable settings (non-null) override settings in context object,
except for attributes and tags, which are merged. |
any
|
n/a
| yes | +| [ecr\_repository\_url](#input\_ecr\_repository\_url) | The URL of the ECR repository where the app image is stored |
string
|
n/a
| yes | +| [image\_version](#input\_image\_version) | The version of the app image to deploy |
string
|
n/a
| yes | +| [keystore\_addr](#input\_keystore\_addr) | The address of the MongoDB instance to use for the persistent keystore |
string
|
n/a
| yes | +| [log\_level](#input\_log\_level) | Defines logging level for the application |
string
|
n/a
| yes | +| [max\_capacity](#input\_max\_capacity) | Maximum number of instances in the autoscaling group |
number
|
8
| no | +| [min\_capacity](#input\_min\_capacity) | Minimum number of instances in the autoscaling group |
number
|
2
| no | +| [port](#input\_port) | The port the app listens on |
number
|
n/a
| yes | +| [private\_subnets](#input\_private\_subnets) | The IDs of the private subnets to deploy to |
list(string)
|
n/a
| yes | +| [prometheus\_endpoint](#input\_prometheus\_endpoint) | The endpoint of the Prometheus server to use for monitoring |
string
|
n/a
| yes | +| [public\_subnets](#input\_public\_subnets) | The IDs of the public subnets to deploy to |
list(string)
|
n/a
| yes | +| [route53\_zones](#input\_route53\_zones) | The FQDNs to use for the app |
map(string)
|
n/a
| yes | +| [route53\_zones\_certificates](#input\_route53\_zones\_certificates) | The ARNs of the ACM certificates to use for HTTPS |
map(string)
|
n/a
| yes | +| [task\_cpu](#input\_task\_cpu) | The number of CPU units to reserve for the container. |
number
|
n/a
| yes | +| [task\_memory](#input\_task\_memory) | The amount of memory (in MiB) to reserve for the container. |
number
|
n/a
| yes | +| [vpc\_id](#input\_vpc\_id) | The ID of the VPC to deploy to |
string
|
n/a
| yes | ## Outputs | Name | Description | |------|-------------| +| [ecs\_cluster\_name](#output\_ecs\_cluster\_name) | The name of the ECS cluster | +| [ecs\_service\_name](#output\_ecs\_service\_name) | The name of the ECS service | +| [ecs\_task\_family](#output\_ecs\_task\_family) | The family of the task definition | | [load\_balancer\_arn](#output\_load\_balancer\_arn) | The ARN of the load balancer | | [load\_balancer\_arn\_suffix](#output\_load\_balancer\_arn\_suffix) | The ARN suffix of the load balancer | | [service\_name](#output\_service\_name) | The name of the service | | [service\_security\_group\_id](#output\_service\_security\_group\_id) | The ID of the security group for the service | | [target\_group\_arn](#output\_target\_group\_arn) | The ARN of the target group | - + + + diff --git a/terraform/ecs/cluster_autoscaling.tf b/terraform/ecs/cluster_autoscaling.tf index 12123e3..2681401 100644 --- a/terraform/ecs/cluster_autoscaling.tf +++ b/terraform/ecs/cluster_autoscaling.tf @@ -22,7 +22,6 @@ resource "aws_appautoscaling_target" "ecs_target" { resource_id = "service/${aws_ecs_cluster.app_cluster.name}/${aws_ecs_service.app_service.name}" scalable_dimension = "ecs:service:DesiredCount" service_namespace = "ecs" - role_arn = aws_iam_role.ecs_autoscaling_role.arn } resource "aws_appautoscaling_policy" "ecs_target_cpu" { diff --git a/terraform/ecs/context.tf b/terraform/ecs/context.tf index 9871e49..da1c290 100644 --- a/terraform/ecs/context.tf +++ b/terraform/ecs/context.tf @@ -23,21 +23,7 @@ module "this" { #tflint-ignore: terraform_standard_module_structure variable "context" { - type = any - default = { - namespace = null - region = null - stage = null - name = null - delimiter = null - attributes = [] - tags = {} - label_order = [] - regex_replace_chars = null - id_length_limit = null - label_key_case = null - label_value_case = null - } + type = any description = <<-EOT Single object for setting entire context at once. See description of individual variables for details. diff --git a/terraform/ecs/outputs.tf b/terraform/ecs/outputs.tf index ac0f3bc..a5d99d6 100644 --- a/terraform/ecs/outputs.tf +++ b/terraform/ecs/outputs.tf @@ -23,6 +23,16 @@ output "load_balancer_arn_suffix" { value = aws_lb.load_balancer.arn_suffix } +output "ecs_cluster_name" { + description = "The name of the ECS cluster" + value = aws_ecs_cluster.app_cluster.name +} + +output "ecs_service_name" { + description = "The name of the ECS service" + value = aws_ecs_service.app_service.name +} + output "ecs_task_family" { description = "The family of the task definition" value = aws_ecs_task_definition.app_task.family diff --git a/terraform/inputs.tf b/terraform/inputs.tf new file mode 100644 index 0000000..21c7766 --- /dev/null +++ b/terraform/inputs.tf @@ -0,0 +1,29 @@ +data "terraform_remote_state" "org" { + backend = "remote" + config = { + organization = "wallet-connect" + workspaces = { + name = "aws-org" + } + } +} + +data "terraform_remote_state" "dns" { + backend = "remote" + config = { + organization = "wallet-connect" + workspaces = { + name = "dns-delegation" + } + } +} + +data "terraform_remote_state" "monitoring" { + backend = "remote" + config = { + organization = "wallet-connect" + workspaces = { + name = "monitoring" + } + } +} diff --git a/terraform/main.tf b/terraform/main.tf index bea5c81..522394f 100644 --- a/terraform/main.tf +++ b/terraform/main.tf @@ -1,40 +1,10 @@ -data "terraform_remote_state" "org" { - backend = "remote" - config = { - organization = "wallet-connect" - workspaces = { - name = "aws-org" - } - } -} - -data "terraform_remote_state" "dns" { - backend = "remote" - config = { - organization = "wallet-connect" - workspaces = { - name = "dns-delegation" - } - } -} - -data "terraform_remote_state" "monitoring" { - backend = "remote" - config = { - organization = "wallet-connect" - workspaces = { - name = "monitoring" - } - } -} - resource "random_pet" "this" { length = 2 } locals { keystore_name = "keystore" - ecr_repository_url = data.terraform_remote_state.org.outputs.accounts-ecr-urls.wl.keys[local.stage] + ecr_repository_url = data.terraform_remote_state.org.outputs.accounts.wl.keys[local.stage].ecr-url stage = lookup({ "keyserver-wl-staging" = "staging", diff --git a/terraform/monitoring/README.md b/terraform/monitoring/README.md index 871600c..26dc7b6 100644 --- a/terraform/monitoring/README.md +++ b/terraform/monitoring/README.md @@ -2,7 +2,8 @@ Configure the Grafana dashboards for the application - + + ## Requirements | Name | Version | @@ -10,14 +11,12 @@ Configure the Grafana dashboards for the application | [terraform](#requirement\_terraform) | >= 1.0 | | [grafana](#requirement\_grafana) | ~> 2.0 | | [jsonnet](#requirement\_jsonnet) | ~> 2.2.0 | - ## Providers | Name | Version | |------|---------| | [grafana](#provider\_grafana) | ~> 2.0 | | [jsonnet](#provider\_jsonnet) | ~> 2.2.0 | - ## Modules | Name | Source | Version | @@ -25,43 +24,23 @@ Configure the Grafana dashboards for the application | [monitoring-role](#module\_monitoring-role) | app.terraform.io/wallet-connect/monitoring-role/aws | 1.0.2 | | [this](#module\_this) | app.terraform.io/wallet-connect/label/null | 0.3.2 | -## Resources - -| Name | Type | -|------|------| -| [grafana_dashboard.main](https://registry.terraform.io/providers/grafana/grafana/latest/docs/resources/dashboard) | resource | -| [grafana_data_source.cloudwatch](https://registry.terraform.io/providers/grafana/grafana/latest/docs/resources/data_source) | resource | -| [grafana_data_source.prometheus](https://registry.terraform.io/providers/grafana/grafana/latest/docs/resources/data_source) | resource | -| [jsonnet_file.dashboard](https://registry.terraform.io/providers/alxrem/jsonnet/latest/docs/data-sources/file) | data source | - ## Inputs - | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| -| [attributes](#input\_attributes) | ID element. Additional attributes (e.g. `workers` or `cluster`) to add to `id`,
in the order they appear in the list. New attributes are appended to the
end of the list. The elements of the list are joined by the `delimiter`
and treated as a single ID element. | `list(string)` | `[]` | no | -| [context](#input\_context) | Single object for setting entire context at once.
See description of individual variables for details.
Leave string and numeric variables as `null` to use default value.
Individual variable settings (non-null) override settings in context object,
except for attributes and tags, which are merged. | `any` |
{
"attributes": [],
"delimiter": null,
"id_length_limit": null,
"label_key_case": null,
"label_order": [],
"label_value_case": null,
"name": null,
"namespace": null,
"regex_replace_chars": null,
"region": null,
"stage": null,
"tags": {}
}
| no | -| [delimiter](#input\_delimiter) | Delimiter to be used between ID elements.
Defaults to `-` (hyphen). Set to `""` to use no delimiter at all. | `string` | `null` | no | -| [ecs\_service\_name](#input\_ecs\_service\_name) | The name of the ECS service. | `string` | n/a | yes | -| [ecs\_target\_group\_arn](#input\_ecs\_target\_group\_arn) | The ARN of the ECS LB target group. | `string` | n/a | yes | -| [id\_length\_limit](#input\_id\_length\_limit) | Limit `id` to this many characters (minimum 6).
Set to `0` for unlimited length.
Set to `null` for keep the existing setting, which defaults to `0`.
Does not affect `id_full`. | `number` | `null` | no | -| [keystore\_cluster\_id](#input\_keystore\_cluster\_id) | The ID of the keystore DocDB cluster. | `string` | n/a | yes | -| [label\_key\_case](#input\_label\_key\_case) | Controls the letter case of the `tags` keys (label names) for tags generated by this module.
Does not affect keys of tags passed in via the `tags` input.
Possible values: `lower`, `title`, `upper`.
Default value: `title`. | `string` | `null` | no | -| [label\_order](#input\_label\_order) | The order in which the labels (ID elements) appear in the `id`.
Defaults to ["namespace", "region", "stage", "name", "attributes"].
You can omit any of the 5 labels, but at least one must be present. | `list(string)` | `null` | no | -| [label\_value\_case](#input\_label\_value\_case) | Controls the letter case of ID elements (labels) as included in `id`,
set as tag values, and output by this module individually.
Does not affect values of tags passed in via the `tags` input.
Possible values: `lower`, `title`, `upper` and `none` (no transformation).
Set this to `title` and set `delimiter` to `""` to yield Pascal Case IDs.
Default value: `lower`. | `string` | `null` | no | -| [load\_balancer\_arn](#input\_load\_balancer\_arn) | The ARN of the load balancer. | `string` | n/a | yes | -| [monitoring\_role\_arn](#input\_monitoring\_role\_arn) | The ARN of the monitoring role. | `string` | n/a | yes | -| [name](#input\_name) | ID element. Usually the component name.
This is the only ID element not also included as a `tag`.
The "name" tag is set to the full `id` string. There is no tag with the value of the `name` input. | `string` | `null` | no | -| [namespace](#input\_namespace) | ID element. Usually the organization name, i.e. 'walletconnect' to help ensure generated IDs are globally unique. | `string` | `null` | no | -| [notification\_channels](#input\_notification\_channels) | The notification channels to send alerts to | `list(any)` | n/a | yes | -| [prometheus\_endpoint](#input\_prometheus\_endpoint) | The endpoint for the Prometheus server. | `string` | n/a | yes | -| [regex\_replace\_chars](#input\_regex\_replace\_chars) | Terraform regular expression (regex) string.
Characters matching the regex will be removed from the ID elements.
If not set, `"/[^a-zA-Z0-9-]/"` is used to remove all characters other than hyphens, letters and digits. | `string` | `null` | no | -| [region](#input\_region) | ID element. Usually used for region e.g. 'uw2', 'us-west-2'. | `string` | `null` | no | -| [stage](#input\_stage) | ID element. Usually used to indicate role, e.g. 'prod', 'staging', 'source', 'build', 'test', 'deploy', 'release'. | `string` | `null` | no | -| [tags](#input\_tags) | Additional tags. | `map(string)` | `{}` | no | - +| [context](#input\_context) | Single object for setting entire context at once.
See description of individual variables for details.
Leave string and numeric variables as `null` to use default value.
Individual variable settings (non-null) override settings in context object,
except for attributes and tags, which are merged. |
any
|
n/a
| yes | +| [ecs\_service\_name](#input\_ecs\_service\_name) | The name of the ECS service. |
string
|
n/a
| yes | +| [ecs\_target\_group\_arn](#input\_ecs\_target\_group\_arn) | The ARN of the ECS LB target group. |
string
|
n/a
| yes | +| [ecs\_task\_family](#input\_ecs\_task\_family) | The name of the ECS task family. |
string
|
n/a
| yes | +| [keystore\_cluster\_id](#input\_keystore\_cluster\_id) | The ID of the keystore DocDB cluster. |
string
|
n/a
| yes | +| [load\_balancer\_arn](#input\_load\_balancer\_arn) | The ARN of the load balancer. |
string
|
n/a
| yes | +| [monitoring\_role\_arn](#input\_monitoring\_role\_arn) | The ARN of the monitoring role. |
string
|
n/a
| yes | +| [notification\_channels](#input\_notification\_channels) | The notification channels to send alerts to |
list(any)
|
n/a
| yes | +| [prometheus\_endpoint](#input\_prometheus\_endpoint) | The endpoint for the Prometheus server. |
string
|
n/a
| yes | ## Outputs | Name | Description | |------|-------------| | [dashboard\_definition](#output\_dashboard\_definition) | The JSON definition of the dashboard. | - + + + diff --git a/terraform/monitoring/context.tf b/terraform/monitoring/context.tf index 9871e49..da1c290 100644 --- a/terraform/monitoring/context.tf +++ b/terraform/monitoring/context.tf @@ -23,21 +23,7 @@ module "this" { #tflint-ignore: terraform_standard_module_structure variable "context" { - type = any - default = { - namespace = null - region = null - stage = null - name = null - delimiter = null - attributes = [] - tags = {} - label_order = [] - regex_replace_chars = null - id_length_limit = null - label_key_case = null - label_value_case = null - } + type = any description = <<-EOT Single object for setting entire context at once. See description of individual variables for details. diff --git a/terraform/monitoring/panels/README.md b/terraform/monitoring/panels/README.md new file mode 100644 index 0000000..f91dc73 --- /dev/null +++ b/terraform/monitoring/panels/README.md @@ -0,0 +1,22 @@ + + +## Requirements + +No requirements. +## Providers + +No providers. +## Modules + +No modules. + +## Inputs + +No inputs. + +## Outputs + +No outputs. + + + diff --git a/terraform/monitoring/panels/docdb/low_mem_op_throttled.libsonnet b/terraform/monitoring/panels/docdb/low_mem_op_throttled.libsonnet index f74e19d..7ea7d99 100644 --- a/terraform/monitoring/panels/docdb/low_mem_op_throttled.libsonnet +++ b/terraform/monitoring/panels/docdb/low_mem_op_throttled.libsonnet @@ -61,7 +61,7 @@ local ops_alert(vars) = alert.new( .setAlert(ops_alert(vars)) .addTarget(targets.cloudwatch( - alias = 'LowMem Num Operations Throttled (Avg)', + alias = 'LowMem Num Operations Throttled (Max)', datasource = ds.cloudwatch, dimensions = { DBClusterIdentifier: vars.docdb_cluster_id diff --git a/terraform/res_application.tf b/terraform/res_application.tf index cbaa28f..e80f115 100644 --- a/terraform/res_application.tf +++ b/terraform/res_application.tf @@ -1,7 +1,3 @@ -resource "aws_prometheus_workspace" "prometheus" { - alias = "prometheus-${module.this.id}" -} - # ECS Cluster, Task, Service, and Load Balancer for our app module "ecs" { source = "./ecs" diff --git a/terraform/res_cloudwatch.tf b/terraform/res_cloudwatch.tf new file mode 100644 index 0000000..4265e55 --- /dev/null +++ b/terraform/res_cloudwatch.tf @@ -0,0 +1,12 @@ +module "cloudwatch" { + source = "./cloudwatch" + context = module.this.context + + webhook_url = var.betterstack_cloudwatch_webhook + + ecs_cluster_name = module.ecs.ecs_cluster_name + ecs_service_name = module.ecs.ecs_service_name + + docdb_cluster_id = module.keystore.cluster_id + docdb_memory_threshold = module.this.stage == "prod" ? 4 : 2 +} diff --git a/terraform/res_prometheus.tf b/terraform/res_prometheus.tf new file mode 100644 index 0000000..f0449a1 --- /dev/null +++ b/terraform/res_prometheus.tf @@ -0,0 +1,28 @@ +resource "aws_prometheus_workspace" "prometheus" { + alias = "prometheus-${module.this.id}" +} + +resource "aws_prometheus_alert_manager_definition" "prometheus_alerts" { + workspace_id = aws_prometheus_workspace.prometheus.id + definition = <<-EOF + alertmanager_config: | + route: + receiver: 'BetterUptime' + receivers: + - name: 'BetterUptime' + sns_configs: + - topic_arn: ${aws_sns_topic.prometheus_webhook.arn} + EOF +} + +#tfsec:ignore:aws-sns-enable-topic-encryption +resource "aws_sns_topic" "prometheus_webhook" { + name = "prometheus-webhook" + display_name = "Prometheus Webhook forwarding to BetterUptime" +} + +resource "aws_sns_topic_subscription" "prometheus_webhook" { + endpoint = var.betterstack_prometheus_webhook + protocol = "https" + topic_arn = aws_sns_topic.prometheus_webhook.arn +} diff --git a/terraform/variables.tf b/terraform/variables.tf index 9cd52ce..0e10df5 100644 --- a/terraform/variables.tf +++ b/terraform/variables.tf @@ -64,3 +64,14 @@ variable "notification_channels" { default = [] } +variable "betterstack_prometheus_webhook" { + description = "The BetterStack webhook to send Prometheus alerts to" + type = string + sensitive = true +} + +variable "betterstack_cloudwatch_webhook" { + description = "The BetterStack webhook to send CloudWatch alerts to" + type = string + sensitive = true +}