Skip to content

Commit 68615e6

Browse files
nx-plugin-for-awsnx-plugin-for-aws
andauthored
fix(ts#rdb): make terraform aurora physical resource names unique (#785) (#795)
The terraform aurora module used static physical names derived only from the database name (e.g. db subnet group 'postgres-db-aurora', cluster identifier 'postgres-db-aurora', proxy 'postgres-db-proxy'). Two deployments of the same database project in one account/region would collide, and leaked resources from an interrupted destroy would block all subsequent deploys with DBSubnetGroupAlreadyExists. Append a random_string suffix to the subnet group, cluster identifier, instance identifiers, final snapshot identifier, credentials rotation stack/Lambda, RDS proxy and backup vault/plan names, matching the approach already used by the app-level rdb module and the CDK construct (which relies on CloudFormation unique physical name generation). This is the root cause of the terraform-deploy-rdb smoke test failures on main: a cancelled CI run leaked 'postgres-db-aurora' and 'my-sql-db-aurora' subnet groups (terraform state was per-run, so the next run could not adopt or delete them) and every subsequent run failed to create the same statically-named subnet groups. Co-authored-by: nx-plugin-for-aws <nx-plugin@amazon.com>
1 parent a89bce1 commit 68615e6

2 files changed

Lines changed: 54 additions & 27 deletions

File tree

packages/nx-plugin/src/ts/rdb/__snapshots__/generator.spec.ts.snap

Lines changed: 36 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1063,10 +1063,19 @@ variable "tags" {
10631063
default = {}
10641064
}
10651065
1066+
# Suffix appended to physical resource names so that multiple deployments
1067+
# (e.g. separate environments in the same account and region) don't collide.
1068+
resource "random_string" "suffix" {
1069+
length = 8
1070+
special = false
1071+
upper = false
1072+
}
1073+
10661074
locals {
10671075
default_port = var.engine == "aurora-mysql" ? 3306 : 5432
10681076
default_engine_version = var.engine == "aurora-mysql" ? "8.0.mysql_aurora.3.12.0" : "17.7"
10691077
parameter_group_family = var.engine == "aurora-mysql" ? "aurora-mysql8.0" : "aurora-postgresql\${split(".", coalesce(var.engine_version, local.default_engine_version))[0]}"
1078+
cluster_name = "\${var.name}-aurora-\${random_string.suffix.result}"
10701079
}
10711080
10721081
resource "aws_rds_cluster_parameter_group" "database" {
@@ -1164,7 +1173,7 @@ resource "aws_security_group" "database" {
11641173
}
11651174
11661175
resource "aws_db_subnet_group" "database" {
1167-
name = "\${var.name}-aurora"
1176+
name = local.cluster_name
11681177
subnet_ids = var.subnet_ids
11691178
11701179
tags = merge(var.tags, {
@@ -1204,7 +1213,7 @@ resource "aws_secretsmanager_secret_version" "credentials" {
12041213
resource "aws_rds_cluster" "database" {
12051214
#checkov:skip=CKV2_AWS_27:Query logging can be enabled with enable_cloudwatch_logs; this module defaults to CDK-equivalent behavior unless logging is requested
12061215
#checkov:skip=CKV_AWS_139:Deletion protection is enabled but checkov is unable to detect it correctly
1207-
cluster_identifier = "\${var.name}-aurora"
1216+
cluster_identifier = local.cluster_name
12081217
engine = var.engine
12091218
engine_version = coalesce(var.engine_version, local.default_engine_version)
12101219
database_name = var.database_name
@@ -1218,7 +1227,7 @@ resource "aws_rds_cluster" "database" {
12181227
kms_key_id = aws_kms_key.database.arn
12191228
deletion_protection = var.deletion_protection
12201229
skip_final_snapshot = var.skip_final_snapshot
1221-
final_snapshot_identifier = var.skip_final_snapshot ? null : "\${var.name}-aurora-final-snapshot"
1230+
final_snapshot_identifier = var.skip_final_snapshot ? null : "\${local.cluster_name}-final-snapshot"
12221231
iam_database_authentication_enabled = true
12231232
monitoring_interval = 5
12241233
monitoring_role_arn = aws_iam_role.enhanced_monitoring.arn
@@ -1238,7 +1247,7 @@ resource "aws_rds_cluster" "database" {
12381247
resource "aws_rds_cluster_instance" "database" {
12391248
count = var.instance_count
12401249
1241-
identifier = "\${var.name}-aurora-\${count.index + 1}"
1250+
identifier = "\${local.cluster_name}-\${count.index + 1}"
12421251
cluster_identifier = aws_rds_cluster.database.id
12431252
instance_class = "db.serverless"
12441253
engine = aws_rds_cluster.database.engine
@@ -1292,7 +1301,7 @@ resource "aws_vpc_security_group_ingress_rule" "rotation_to_database" {
12921301
resource "aws_cloudformation_stack" "credentials_rotation" {
12931302
#checkov:skip=CKV_AWS_124:SNS event notifications are not required for the credentials rotation stack
12941303
count = var.enable_credential_rotation ? 1 : 0
1295-
name = "\${var.name}-aurora-credentials-rotation"
1304+
name = "\${local.cluster_name}-credentials-rotation"
12961305
12971306
capabilities = ["CAPABILITY_IAM", "CAPABILITY_AUTO_EXPAND"]
12981307
@@ -1306,7 +1315,7 @@ resource "aws_cloudformation_stack" "credentials_rotation" {
13061315
SecretId = aws_secretsmanager_secret.credentials.arn
13071316
HostedRotationLambda = {
13081317
RotationType = var.engine == "aurora-mysql" ? "MySQLSingleUser" : "PostgreSQLSingleUser"
1309-
RotationLambdaName = "\${var.name}-aurora-credentials-rotation"
1318+
RotationLambdaName = "\${local.cluster_name}-credentials-rotation"
13101319
VpcSecurityGroupIds = aws_security_group.rotation[0].id
13111320
VpcSubnetIds = join(",", var.lambda_subnet_ids)
13121321
}
@@ -1386,7 +1395,7 @@ resource "aws_security_group" "proxy" {
13861395
resource "aws_db_proxy" "aurora" {
13871396
count = var.enable_rds_proxy ? 1 : 0
13881397
1389-
name = "\${var.name}-proxy"
1398+
name = "\${var.name}-proxy-\${random_string.suffix.result}"
13901399
default_auth_scheme = "IAM_AUTH"
13911400
engine_family = var.engine == "aurora-mysql" ? "MYSQL" : "POSTGRESQL"
13921401
role_arn = aws_iam_role.proxy[0].arn
@@ -1532,15 +1541,15 @@ resource "aws_iam_role_policy_attachment" "backup" {
15321541
resource "aws_backup_vault" "database" {
15331542
count = var.enable_backup ? 1 : 0
15341543
1535-
name = "\${var.name}-aurora-backup"
1544+
name = "\${local.cluster_name}-backup"
15361545
kms_key_arn = aws_kms_key.database.arn
15371546
tags = var.tags
15381547
}
15391548
15401549
resource "aws_backup_plan" "database" {
15411550
count = var.enable_backup ? 1 : 0
15421551
1543-
name = "\${var.name}-aurora-backup"
1552+
name = "\${local.cluster_name}-backup"
15441553
15451554
rule {
15461555
rule_name = "daily-backup"
@@ -2513,10 +2522,19 @@ variable "tags" {
25132522
default = {}
25142523
}
25152524
2525+
# Suffix appended to physical resource names so that multiple deployments
2526+
# (e.g. separate environments in the same account and region) don't collide.
2527+
resource "random_string" "suffix" {
2528+
length = 8
2529+
special = false
2530+
upper = false
2531+
}
2532+
25162533
locals {
25172534
default_port = var.engine == "aurora-mysql" ? 3306 : 5432
25182535
default_engine_version = var.engine == "aurora-mysql" ? "8.0.mysql_aurora.3.12.0" : "17.7"
25192536
parameter_group_family = var.engine == "aurora-mysql" ? "aurora-mysql8.0" : "aurora-postgresql\${split(".", coalesce(var.engine_version, local.default_engine_version))[0]}"
2537+
cluster_name = "\${var.name}-aurora-\${random_string.suffix.result}"
25202538
}
25212539
25222540
resource "aws_rds_cluster_parameter_group" "database" {
@@ -2614,7 +2632,7 @@ resource "aws_security_group" "database" {
26142632
}
26152633
26162634
resource "aws_db_subnet_group" "database" {
2617-
name = "\${var.name}-aurora"
2635+
name = local.cluster_name
26182636
subnet_ids = var.subnet_ids
26192637
26202638
tags = merge(var.tags, {
@@ -2654,7 +2672,7 @@ resource "aws_secretsmanager_secret_version" "credentials" {
26542672
resource "aws_rds_cluster" "database" {
26552673
#checkov:skip=CKV2_AWS_27:Query logging can be enabled with enable_cloudwatch_logs; this module defaults to CDK-equivalent behavior unless logging is requested
26562674
#checkov:skip=CKV_AWS_139:Deletion protection is enabled but checkov is unable to detect it correctly
2657-
cluster_identifier = "\${var.name}-aurora"
2675+
cluster_identifier = local.cluster_name
26582676
engine = var.engine
26592677
engine_version = coalesce(var.engine_version, local.default_engine_version)
26602678
database_name = var.database_name
@@ -2668,7 +2686,7 @@ resource "aws_rds_cluster" "database" {
26682686
kms_key_id = aws_kms_key.database.arn
26692687
deletion_protection = var.deletion_protection
26702688
skip_final_snapshot = var.skip_final_snapshot
2671-
final_snapshot_identifier = var.skip_final_snapshot ? null : "\${var.name}-aurora-final-snapshot"
2689+
final_snapshot_identifier = var.skip_final_snapshot ? null : "\${local.cluster_name}-final-snapshot"
26722690
iam_database_authentication_enabled = true
26732691
monitoring_interval = 5
26742692
monitoring_role_arn = aws_iam_role.enhanced_monitoring.arn
@@ -2688,7 +2706,7 @@ resource "aws_rds_cluster" "database" {
26882706
resource "aws_rds_cluster_instance" "database" {
26892707
count = var.instance_count
26902708
2691-
identifier = "\${var.name}-aurora-\${count.index + 1}"
2709+
identifier = "\${local.cluster_name}-\${count.index + 1}"
26922710
cluster_identifier = aws_rds_cluster.database.id
26932711
instance_class = "db.serverless"
26942712
engine = aws_rds_cluster.database.engine
@@ -2742,7 +2760,7 @@ resource "aws_vpc_security_group_ingress_rule" "rotation_to_database" {
27422760
resource "aws_cloudformation_stack" "credentials_rotation" {
27432761
#checkov:skip=CKV_AWS_124:SNS event notifications are not required for the credentials rotation stack
27442762
count = var.enable_credential_rotation ? 1 : 0
2745-
name = "\${var.name}-aurora-credentials-rotation"
2763+
name = "\${local.cluster_name}-credentials-rotation"
27462764
27472765
capabilities = ["CAPABILITY_IAM", "CAPABILITY_AUTO_EXPAND"]
27482766
@@ -2756,7 +2774,7 @@ resource "aws_cloudformation_stack" "credentials_rotation" {
27562774
SecretId = aws_secretsmanager_secret.credentials.arn
27572775
HostedRotationLambda = {
27582776
RotationType = var.engine == "aurora-mysql" ? "MySQLSingleUser" : "PostgreSQLSingleUser"
2759-
RotationLambdaName = "\${var.name}-aurora-credentials-rotation"
2777+
RotationLambdaName = "\${local.cluster_name}-credentials-rotation"
27602778
VpcSecurityGroupIds = aws_security_group.rotation[0].id
27612779
VpcSubnetIds = join(",", var.lambda_subnet_ids)
27622780
}
@@ -2836,7 +2854,7 @@ resource "aws_security_group" "proxy" {
28362854
resource "aws_db_proxy" "aurora" {
28372855
count = var.enable_rds_proxy ? 1 : 0
28382856
2839-
name = "\${var.name}-proxy"
2857+
name = "\${var.name}-proxy-\${random_string.suffix.result}"
28402858
default_auth_scheme = "IAM_AUTH"
28412859
engine_family = var.engine == "aurora-mysql" ? "MYSQL" : "POSTGRESQL"
28422860
role_arn = aws_iam_role.proxy[0].arn
@@ -2982,15 +3000,15 @@ resource "aws_iam_role_policy_attachment" "backup" {
29823000
resource "aws_backup_vault" "database" {
29833001
count = var.enable_backup ? 1 : 0
29843002
2985-
name = "\${var.name}-aurora-backup"
3003+
name = "\${local.cluster_name}-backup"
29863004
kms_key_arn = aws_kms_key.database.arn
29873005
tags = var.tags
29883006
}
29893007
29903008
resource "aws_backup_plan" "database" {
29913009
count = var.enable_backup ? 1 : 0
29923010
2993-
name = "\${var.name}-aurora-backup"
3011+
name = "\${local.cluster_name}-backup"
29943012
29953013
rule {
29963014
rule_name = "daily-backup"

packages/nx-plugin/src/utils/rdb-constructs/files/terraform/core/rdb/aurora/aurora.tf.template

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -159,10 +159,19 @@ variable "tags" {
159159
default = {}
160160
}
161161

162+
# Suffix appended to physical resource names so that multiple deployments
163+
# (e.g. separate environments in the same account and region) don't collide.
164+
resource "random_string" "suffix" {
165+
length = 8
166+
special = false
167+
upper = false
168+
}
169+
162170
locals {
163171
default_port = var.engine == "aurora-mysql" ? 3306 : 5432
164172
default_engine_version = var.engine == "aurora-mysql" ? "8.0.mysql_aurora.3.12.0" : "17.7"
165173
parameter_group_family = var.engine == "aurora-mysql" ? "aurora-mysql8.0" : "aurora-postgresql${split(".", coalesce(var.engine_version, local.default_engine_version))[0]}"
174+
cluster_name = "${var.name}-aurora-${random_string.suffix.result}"
166175
}
167176

168177
resource "aws_rds_cluster_parameter_group" "database" {
@@ -260,7 +269,7 @@ resource "aws_security_group" "database" {
260269
}
261270

262271
resource "aws_db_subnet_group" "database" {
263-
name = "${var.name}-aurora"
272+
name = local.cluster_name
264273
subnet_ids = var.subnet_ids
265274

266275
tags = merge(var.tags, {
@@ -300,7 +309,7 @@ resource "aws_secretsmanager_secret_version" "credentials" {
300309
resource "aws_rds_cluster" "database" {
301310
#checkov:skip=CKV2_AWS_27:Query logging can be enabled with enable_cloudwatch_logs; this module defaults to CDK-equivalent behavior unless logging is requested
302311
#checkov:skip=CKV_AWS_139:Deletion protection is enabled but checkov is unable to detect it correctly
303-
cluster_identifier = "${var.name}-aurora"
312+
cluster_identifier = local.cluster_name
304313
engine = var.engine
305314
engine_version = coalesce(var.engine_version, local.default_engine_version)
306315
database_name = var.database_name
@@ -314,7 +323,7 @@ resource "aws_rds_cluster" "database" {
314323
kms_key_id = aws_kms_key.database.arn
315324
deletion_protection = var.deletion_protection
316325
skip_final_snapshot = var.skip_final_snapshot
317-
final_snapshot_identifier = var.skip_final_snapshot ? null : "${var.name}-aurora-final-snapshot"
326+
final_snapshot_identifier = var.skip_final_snapshot ? null : "${local.cluster_name}-final-snapshot"
318327
iam_database_authentication_enabled = true
319328
monitoring_interval = 5
320329
monitoring_role_arn = aws_iam_role.enhanced_monitoring.arn
@@ -334,7 +343,7 @@ resource "aws_rds_cluster" "database" {
334343
resource "aws_rds_cluster_instance" "database" {
335344
count = var.instance_count
336345

337-
identifier = "${var.name}-aurora-${count.index + 1}"
346+
identifier = "${local.cluster_name}-${count.index + 1}"
338347
cluster_identifier = aws_rds_cluster.database.id
339348
instance_class = "db.serverless"
340349
engine = aws_rds_cluster.database.engine
@@ -388,7 +397,7 @@ resource "aws_vpc_security_group_ingress_rule" "rotation_to_database" {
388397
resource "aws_cloudformation_stack" "credentials_rotation" {
389398
#checkov:skip=CKV_AWS_124:SNS event notifications are not required for the credentials rotation stack
390399
count = var.enable_credential_rotation ? 1 : 0
391-
name = "${var.name}-aurora-credentials-rotation"
400+
name = "${local.cluster_name}-credentials-rotation"
392401

393402
capabilities = ["CAPABILITY_IAM", "CAPABILITY_AUTO_EXPAND"]
394403

@@ -402,7 +411,7 @@ resource "aws_cloudformation_stack" "credentials_rotation" {
402411
SecretId = aws_secretsmanager_secret.credentials.arn
403412
HostedRotationLambda = {
404413
RotationType = var.engine == "aurora-mysql" ? "MySQLSingleUser" : "PostgreSQLSingleUser"
405-
RotationLambdaName = "${var.name}-aurora-credentials-rotation"
414+
RotationLambdaName = "${local.cluster_name}-credentials-rotation"
406415
VpcSecurityGroupIds = aws_security_group.rotation[0].id
407416
VpcSubnetIds = join(",", var.lambda_subnet_ids)
408417
}
@@ -482,7 +491,7 @@ resource "aws_security_group" "proxy" {
482491
resource "aws_db_proxy" "aurora" {
483492
count = var.enable_rds_proxy ? 1 : 0
484493

485-
name = "${var.name}-proxy"
494+
name = "${var.name}-proxy-${random_string.suffix.result}"
486495
default_auth_scheme = "IAM_AUTH"
487496
engine_family = var.engine == "aurora-mysql" ? "MYSQL" : "POSTGRESQL"
488497
role_arn = aws_iam_role.proxy[0].arn
@@ -628,15 +637,15 @@ resource "aws_iam_role_policy_attachment" "backup" {
628637
resource "aws_backup_vault" "database" {
629638
count = var.enable_backup ? 1 : 0
630639

631-
name = "${var.name}-aurora-backup"
640+
name = "${local.cluster_name}-backup"
632641
kms_key_arn = aws_kms_key.database.arn
633642
tags = var.tags
634643
}
635644

636645
resource "aws_backup_plan" "database" {
637646
count = var.enable_backup ? 1 : 0
638647

639-
name = "${var.name}-aurora-backup"
648+
name = "${local.cluster_name}-backup"
640649

641650
rule {
642651
rule_name = "daily-backup"

0 commit comments

Comments
 (0)