cds-snc / forms-terraform

Infrastructure as Code for the GC Forms environment
MIT License
16 stars 7 forks source link

feat: use RDS Proxy for IdP database connection pool #788

Closed patheard closed 2 months ago

patheard commented 3 months ago

Summary

Add an RDS Proxy to manage the database connections for Zitadel. This will help reduce strain on the IdP and database and allow for smoother load scaling.

Related

github-actions[bot] commented 2 months ago

⚠ Terrform update available

Terraform: 1.9.5 (using 1.9.2)
Terragrunt: 0.67.3 (using 0.63.2)
github-actions[bot] commented 2 months ago

Staging: idp

✅   Terraform Init: success ✅   Terraform Validate: success ✅   Terraform Format: success ✅   Terraform Plan: success ✅   Conftest: success

Plan: 13 to add, 4 to change, 0 to destroy
Show summary | CHANGE | NAME | |--------|------------------------------------------------------------------------------------| | add | `aws_secretsmanager_secret.zidatel_database_proxy_auth` | | | `aws_secretsmanager_secret_version.zidatel_database_proxy_auth` | | | `module.idp_database.aws_cloudwatch_log_group.proxy[0]` | | | `module.idp_database.aws_db_proxy.proxy[0]` | | | `module.idp_database.aws_db_proxy_default_target_group.this[0]` | | | `module.idp_database.aws_db_proxy_target.target[0]` | | | `module.idp_database.aws_iam_policy.read_connection_string[0]` | | | `module.idp_database.aws_iam_role.rds_proxy[0]` | | | `module.idp_database.aws_iam_role_policy_attachment.read_connection_string[0]` | | | `module.idp_database.aws_secretsmanager_secret.connection_string[0]` | | | `module.idp_database.aws_secretsmanager_secret.proxy_connection_string[0]` | | | `module.idp_database.aws_secretsmanager_secret_version.connection_string[0]` | | | `module.idp_database.aws_secretsmanager_secret_version.proxy_connection_string[0]` | | update | `aws_ssm_parameter.zitadel_database_host` | | | `module.idp_database.aws_security_group.rds` | | | `module.idp_ecs.aws_ecs_service.this` | | | `module.idp_ecs.aws_iam_policy.this_task_exec` |
Show plan ```terraform Resource actions are indicated with the following symbols: + create ~ update in-place <= read (data resources) Terraform will perform the following actions: # data.aws_iam_policy_document.ecs_task_ssm_parameters will be read during apply # (depends on a resource or a module with changes pending) <= data "aws_iam_policy_document" "ecs_task_ssm_parameters" { + id = (known after apply) + json = (known after apply) + minified_json = (known after apply) + statement { + actions = [ + "ssm:GetParameter", + "ssm:GetParameters", ] + effect = "Allow" + resources = [ + "arn:aws:ssm:ca-central-1:687401027353:parameter/idp_database_cluster_admin_password", + "arn:aws:ssm:ca-central-1:687401027353:parameter/idp_database_cluster_admin_username", + "arn:aws:ssm:ca-central-1:687401027353:parameter/zitadel_admin_password", + "arn:aws:ssm:ca-central-1:687401027353:parameter/zitadel_admin_username", + "arn:aws:ssm:ca-central-1:687401027353:parameter/zitadel_database_host", + "arn:aws:ssm:ca-central-1:687401027353:parameter/zitadel_database_name", + "arn:aws:ssm:ca-central-1:687401027353:parameter/zitadel_database_user_password", + "arn:aws:ssm:ca-central-1:687401027353:parameter/zitadel_database_user_username", + "arn:aws:ssm:ca-central-1:687401027353:parameter/zitadel_secret_key", ] + sid = "GetSSMParameters" } } # aws_secretsmanager_secret.zidatel_database_proxy_auth will be created + resource "aws_secretsmanager_secret" "zidatel_database_proxy_auth" { + arn = (known after apply) + force_overwrite_replica_secret = false + id = (known after apply) + name = "zidatel_database_proxy_auth" + name_prefix = (known after apply) + policy = (known after apply) + recovery_window_in_days = 30 + tags = { + "CostCentre" = "forms-platform-staging" + "Terraform" = "true" } + tags_all = { + "CostCentre" = "forms-platform-staging" + "Terraform" = "true" } + replica (known after apply) } # aws_secretsmanager_secret_version.zidatel_database_proxy_auth will be created + resource "aws_secretsmanager_secret_version" "zidatel_database_proxy_auth" { + arn = (known after apply) + id = (known after apply) + secret_id = (known after apply) + secret_string = (sensitive value) + version_id = (known after apply) + version_stages = (known after apply) } # aws_ssm_parameter.zitadel_database_host will be updated in-place ~ resource "aws_ssm_parameter" "zitadel_database_host" { id = "zitadel_database_host" + insecure_value = (known after apply) name = "zitadel_database_host" tags = { "CostCentre" = "forms-platform-staging" "Terraform" = "true" } ~ value = (sensitive value) ~ version = 1 -> (known after apply) # (8 unchanged attributes hidden) } # module.idp_database.data.aws_iam_policy_document.read_connection_string[0] will be read during apply # (config refers to values not yet known) <= data "aws_iam_policy_document" "read_connection_string" { + id = (known after apply) + json = (known after apply) + minified_json = (known after apply) + statement { + actions = [ + "secretsmanager:DescribeSecret", + "secretsmanager:GetResourcePolicy", + "secretsmanager:GetSecretValue", + "secretsmanager:ListSecretVersionIds", ] + effect = "Allow" + resources = [ + (known after apply), + (known after apply), ] + sid = "0" } + statement { + actions = [ + "secretsmanager:ListSecrets", ] + effect = "Allow" + resources = [ + "*", ] + sid = "1" } + statement { + actions = [ + "kms:Decrypt", ] + effect = "Allow" + resources = [ + "*", ] + sid = "2" + condition { + test = "StringEquals" + values = [ + "secretsmanager.ca-central-1.amazonaws.com", ] + variable = "kms:ViaService" } } } # module.idp_database.aws_cloudwatch_log_group.proxy[0] will be created + resource "aws_cloudwatch_log_group" "proxy" { + arn = (known after apply) + id = (known after apply) + log_group_class = (known after apply) + name = "/aws/rds/proxy/idp-proxy" + name_prefix = (known after apply) + retention_in_days = 14 + skip_destroy = false + tags = { + "CostCentre" = "forms-platform-staging" + "Name" = "idp_proxy_logs" + "Terraform" = "true" } + tags_all = { + "CostCentre" = "forms-platform-staging" + "Name" = "idp_proxy_logs" + "Terraform" = "true" } } # module.idp_database.aws_db_proxy.proxy[0] will be created + resource "aws_db_proxy" "proxy" { + arn = (known after apply) + debug_logging = false + endpoint = (known after apply) + engine_family = "POSTGRESQL" + id = (known after apply) + idle_client_timeout = 1800 + name = "idp-proxy" + require_tls = true + role_arn = (known after apply) + tags = { + "CostCentre" = "forms-platform-staging" + "Name" = "idp-rds-proxy" + "Terraform" = "true" } + tags_all = { + "CostCentre" = "forms-platform-staging" + "Name" = "idp-rds-proxy" + "Terraform" = "true" } + vpc_security_group_ids = [ + "sg-05fac172f4e466ffa", + "sg-0cfa825ebfee9088e", ] + vpc_subnet_ids = [ + "subnet-07e38df0760d389d1", + "subnet-07f9debd31e48ce64", + "subnet-0af8e6e3cf80f582d", ] + auth { + auth_scheme = "SECRETS" + client_password_auth_type = (known after apply) + description = "Additional proxy authentication" + iam_auth = "DISABLED" + secret_arn = (known after apply) # (1 unchanged attribute hidden) } + auth { + auth_scheme = "SECRETS" + client_password_auth_type = (known after apply) + description = "The database connection string" + iam_auth = "DISABLED" + secret_arn = (known after apply) # (1 unchanged attribute hidden) } } # module.idp_database.aws_db_proxy_default_target_group.this[0] will be created + resource "aws_db_proxy_default_target_group" "this" { + arn = (known after apply) + db_proxy_name = "idp-proxy" + id = (known after apply) + name = (known after apply) + connection_pool_config (known after apply) } # module.idp_database.aws_db_proxy_target.target[0] will be created + resource "aws_db_proxy_target" "target" { + db_cluster_identifier = "idp-cluster" + db_proxy_name = "idp-proxy" + endpoint = (known after apply) + id = (known after apply) + port = (known after apply) + rds_resource_id = (known after apply) + target_arn = (known after apply) + target_group_name = (known after apply) + tracked_cluster_id = (known after apply) + type = (known after apply) } # module.idp_database.aws_iam_policy.read_connection_string[0] will be created + resource "aws_iam_policy" "read_connection_string" { + arn = (known after apply) + attachment_count = (known after apply) + id = (known after apply) + name = "idpReadConnectionString" + name_prefix = (known after apply) + path = "/" + policy = (known after apply) + policy_id = (known after apply) + tags = { + "CostCentre" = "forms-platform-staging" + "Terraform" = "true" } + tags_all = { + "CostCentre" = "forms-platform-staging" + "Terraform" = "true" } } # module.idp_database.aws_iam_role.rds_proxy[0] will be created + resource "aws_iam_role" "rds_proxy" { + arn = (known after apply) + assume_role_policy = jsonencode( { + Statement = [ + { + Action = "sts:AssumeRole" + Effect = "Allow" + Principal = { + Service = "rds.amazonaws.com" } + Sid = "RDSAssume" }, ] + Version = "2012-10-17" } ) + create_date = (known after apply) + force_detach_policies = false + id = (known after apply) + managed_policy_arns = (known after apply) + max_session_duration = 3600 + name = "idp_rds_proxy" + name_prefix = (known after apply) + path = "/" + tags = { + "CostCentre" = "forms-platform-staging" + "Terraform" = "true" } + tags_all = { + "CostCentre" = "forms-platform-staging" + "Terraform" = "true" } + unique_id = (known after apply) + inline_policy (known after apply) } # module.idp_database.aws_iam_role_policy_attachment.read_connection_string[0] will be created + resource "aws_iam_role_policy_attachment" "read_connection_string" { + id = (known after apply) + policy_arn = (known after apply) + role = "idp_rds_proxy" } # module.idp_database.aws_secretsmanager_secret.connection_string[0] will be created + resource "aws_secretsmanager_secret" "connection_string" { + arn = (known after apply) + force_overwrite_replica_secret = false + id = (known after apply) + name = "idp-6ky1lc" + name_prefix = (known after apply) + policy = (known after apply) + recovery_window_in_days = 30 + tags = { + "CostCentre" = "forms-platform-staging" + "Terraform" = "true" } + tags_all = { + "CostCentre" = "forms-platform-staging" + "Terraform" = "true" } + replica (known after apply) } # module.idp_database.aws_secretsmanager_secret.proxy_connection_string[0] will be created + resource "aws_secretsmanager_secret" "proxy_connection_string" { + arn = (known after apply) + force_overwrite_replica_secret = false + id = (known after apply) + name = "idp-6ky1lc-proxy-connection-string" + name_prefix = (known after apply) + policy = (known after apply) + recovery_window_in_days = 30 + tags = { + "CostCentre" = "forms-platform-staging" + "Terraform" = "true" } + tags_all = { + "CostCentre" = "forms-platform-staging" + "Terraform" = "true" } + replica (known after apply) } # module.idp_database.aws_secretsmanager_secret_version.connection_string[0] will be created + resource "aws_secretsmanager_secret_version" "connection_string" { + arn = (known after apply) + id = (known after apply) + secret_id = (known after apply) + secret_string = (sensitive value) + version_id = (known after apply) + version_stages = (known after apply) } # module.idp_database.aws_secretsmanager_secret_version.proxy_connection_string[0] will be created + resource "aws_secretsmanager_secret_version" "proxy_connection_string" { + arn = (known after apply) + id = (known after apply) + secret_id = (known after apply) + secret_string = (sensitive value) + version_id = (known after apply) + version_stages = (known after apply) } # module.idp_database.aws_security_group.rds will be updated in-place ~ resource "aws_security_group" "rds" { id = "sg-0cfa825ebfee9088e" name = "idp_rds_sg" ~ tags = { "CostCentre" = "forms-platform-staging" ~ "Name" = "idp_rds_sg" -> "idp_rds_proxy_sg" "Terraform" = "true" } ~ tags_all = { ~ "Name" = "idp_rds_sg" -> "idp_rds_proxy_sg" # (2 unchanged elements hidden) } # (8 unchanged attributes hidden) } # module.idp_ecs.data.aws_iam_policy_document.this_task_exec_combined will be read during apply # (config refers to values not yet known) <= data "aws_iam_policy_document" "this_task_exec_combined" { + id = (known after apply) + json = (known after apply) + minified_json = (known after apply) + source_policy_documents = [ + jsonencode( { + Statement = [ + { + Action = [ + "ecr:GetDownloadUrlForLayer", + "ecr:GetAuthorizationToken", + "ecr:BatchGetImage", + "ecr:BatchCheckLayerAvailability", ] + Effect = "Allow" + Resource = "*" }, ] + Version = "2012-10-17" } ), + jsonencode( { + Statement = [ + { + Action = "logs:DescribeLogGroups" + Effect = "Allow" + Resource = "*" }, + { + Action = [ + "logs:PutLogEvents", + "logs:DescribeLogStreams", + "logs:CreateLogStream", ] + Effect = "Allow" + Resource = "arn:aws:logs:ca-central-1:687401027353:log-group:/aws/ecs/idp/zitadel:*" }, ] + Version = "2012-10-17" } ), + (known after apply), ] } # module.idp_ecs.aws_ecs_service.this will be updated in-place ~ resource "aws_ecs_service" "this" { id = "arn:aws:ecs:ca-central-1:687401027353:service/idp/zitadel" name = "zitadel" tags = { "CostCentre" = "forms-platform-staging" "Terraform" = "true" } ~ task_definition = "zitadel:2" -> "zitadel" # (15 unchanged attributes hidden) # (5 unchanged blocks hidden) } # module.idp_ecs.aws_iam_policy.this_task_exec will be updated in-place ~ resource "aws_iam_policy" "this_task_exec" { id = "arn:aws:iam::687401027353:policy/zitadel_ecs_task_exec_policy" name = "zitadel_ecs_task_exec_policy" ~ policy = jsonencode( { - Statement = [ - { - Action = [ - "ecr:GetDownloadUrlForLayer", - "ecr:GetAuthorizationToken", - "ecr:BatchGetImage", - "ecr:BatchCheckLayerAvailability", ] - Effect = "Allow" - Resource = "*" }, - { - Action = "logs:DescribeLogGroups" - Effect = "Allow" - Resource = "*" }, - { - Action = [ - "logs:PutLogEvents", - "logs:DescribeLogStreams", - "logs:CreateLogStream", ] - Effect = "Allow" - Resource = "arn:aws:logs:ca-central-1:687401027353:log-group:/aws/ecs/idp/zitadel:*" }, - { - Action = [ - "ssm:GetParameters", - "ssm:GetParameter", ] - Effect = "Allow" - Resource = [ - "arn:aws:ssm:ca-central-1:687401027353:parameter/zitadel_secret_key", - "arn:aws:ssm:ca-central-1:687401027353:parameter/zitadel_database_user_username", - "arn:aws:ssm:ca-central-1:687401027353:parameter/zitadel_database_user_password", - "arn:aws:ssm:ca-central-1:687401027353:parameter/zitadel_database_name", - "arn:aws:ssm:ca-central-1:687401027353:parameter/zitadel_database_host", - "arn:aws:ssm:ca-central-1:687401027353:parameter/zitadel_admin_username", - "arn:aws:ssm:ca-central-1:687401027353:parameter/zitadel_admin_password", - "arn:aws:ssm:ca-central-1:687401027353:parameter/idp_database_cluster_admin_username", - "arn:aws:ssm:ca-central-1:687401027353:parameter/idp_database_cluster_admin_password", ] - Sid = "GetSSMParameters" }, ] - Version = "2012-10-17" } ) -> (known after apply) tags = { "CostCentre" = "forms-platform-staging" "Terraform" = "true" } # (7 unchanged attributes hidden) } Plan: 13 to add, 4 to change, 0 to destroy. ───────────────────────────────────────────────────────────────────────────── Saved the plan to: plan.tfplan To perform exactly these actions, run the following command to apply: terraform apply "plan.tfplan" ```
Show Conftest results ```sh WARN - plan.json - main - Missing Common Tags: ["aws_alb_listener_rule.idp_protocol_version"] WARN - plan.json - main - Missing Common Tags: ["aws_iam_policy.idp_send_email"] WARN - plan.json - main - Missing Common Tags: ["aws_iam_user.idp_send_email"] WARN - plan.json - main - Missing Common Tags: ["aws_shield_protection.idp"] 23 tests, 19 passed, 4 warnings, 0 failures, 0 exceptions ```