From e96f3cd2ea92911399e73e8ba6bbc1e2c25b6952 Mon Sep 17 00:00:00 2001 From: Mehmet Gungoren Date: Sun, 11 Feb 2024 02:26:39 +0300 Subject: [PATCH 1/8] add domain and domain_iam_role_name to rds_cluster --- internal/service/rds/cluster.go | 53 ++++++++++++ internal/service/rds/cluster_test.go | 115 +++++++++++++++++++++++++++ 2 files changed, 168 insertions(+) diff --git a/internal/service/rds/cluster.go b/internal/service/rds/cluster.go index 9f2e8f4cea8..d2929d586c4 100644 --- a/internal/service/rds/cluster.go +++ b/internal/service/rds/cluster.go @@ -175,6 +175,14 @@ func ResourceCluster() *schema.Resource { Type: schema.TypeBool, Optional: true, }, + "domain": { + Type: schema.TypeString, + Optional: true, + }, + "domain_iam_role_name": { + Type: schema.TypeString, + Optional: true, + }, "enable_global_write_forwarding": { Type: schema.TypeBool, Optional: true, @@ -614,6 +622,14 @@ func resourceClusterCreate(ctx context.Context, d *schema.ResourceData, meta int input.DBSubnetGroupName = aws.String(v.(string)) } + if v, ok := d.GetOk("domain"); ok { + input.Domain = aws.String(v.(string)) + } + + if v, ok := d.GetOk("domain_iam_role_name"); ok { + input.DomainIAMRoleName = aws.String(v.(string)) + } + if v, ok := d.GetOk("enabled_cloudwatch_logs_exports"); ok && v.(*schema.Set).Len() > 0 { input.EnableCloudwatchLogsExports = flex.ExpandStringSet(v.(*schema.Set)) } @@ -732,6 +748,14 @@ func resourceClusterCreate(ctx context.Context, d *schema.ResourceData, meta int input.DBSubnetGroupName = aws.String(v.(string)) } + if v, ok := d.GetOk("domain"); ok { + input.Domain = aws.String(v.(string)) + } + + if v, ok := d.GetOk("domain_iam_role_name"); ok { + input.DomainIAMRoleName = aws.String(v.(string)) + } + if v, ok := d.GetOk("enabled_cloudwatch_logs_exports"); ok && v.(*schema.Set).Len() > 0 { input.EnableCloudwatchLogsExports = flex.ExpandStringSet(v.(*schema.Set)) } @@ -848,6 +872,14 @@ func resourceClusterCreate(ctx context.Context, d *schema.ResourceData, meta int input.DBSubnetGroupName = aws.String(v.(string)) } + if v, ok := d.GetOk("domain"); ok { + input.Domain = aws.String(v.(string)) + } + + if v, ok := d.GetOk("domain_iam_role_name"); ok { + input.DomainIAMRoleName = aws.String(v.(string)) + } + if v, ok := d.GetOk("enabled_cloudwatch_logs_exports"); ok && v.(*schema.Set).Len() > 0 { input.EnableCloudwatchLogsExports = flex.ExpandStringSet(v.(*schema.Set)) } @@ -966,6 +998,14 @@ func resourceClusterCreate(ctx context.Context, d *schema.ResourceData, meta int input.DBSystemId = aws.String(v.(string)) } + if v, ok := d.GetOk("domain"); ok { + input.Domain = aws.String(v.(string)) + } + + if v, ok := d.GetOk("domain_iam_role_name"); ok { + input.DomainIAMRoleName = aws.String(v.(string)) + } + if v, ok := d.GetOk("enable_global_write_forwarding"); ok { input.EnableGlobalWriteForwarding = aws.Bool(v.(bool)) } @@ -1144,6 +1184,14 @@ func resourceClusterRead(ctx context.Context, d *schema.ResourceData, meta inter d.Set("db_subnet_group_name", dbc.DBSubnetGroup) d.Set("db_system_id", dbc.DBSystemId) d.Set("deletion_protection", dbc.DeletionProtection) + if len(dbc.DomainMemberships) > 0 && dbc.DomainMemberships[0] != nil { + domainMembership := dbc.DomainMemberships[0] + d.Set("domain", domainMembership.Domain) + d.Set("domain_iam_role_name", domainMembership.IAMRoleName) + } else { + d.Set("domain", nil) + d.Set("domain_iam_role_name", nil) + } d.Set("enabled_cloudwatch_logs_exports", aws.StringValueSlice(dbc.EnabledCloudwatchLogsExports)) d.Set("enable_http_endpoint", dbc.HttpEndpointEnabled) d.Set("endpoint", dbc.Endpoint) @@ -1286,6 +1334,11 @@ func resourceClusterUpdate(ctx context.Context, d *schema.ResourceData, meta int input.DeletionProtection = aws.Bool(d.Get("deletion_protection").(bool)) } + if d.HasChanges("domain", "domain_iam_role_name") { + input.Domain = aws.String(d.Get("domain").(string)) + input.DomainIAMRoleName = aws.String(d.Get("domain_iam_role_name").(string)) + } + if d.HasChange("enable_global_write_forwarding") { input.EnableGlobalWriteForwarding = aws.Bool(d.Get("enable_global_write_forwarding").(bool)) } diff --git a/internal/service/rds/cluster_test.go b/internal/service/rds/cluster_test.go index 016b6db7e4d..56e835f0985 100644 --- a/internal/service/rds/cluster_test.go +++ b/internal/service/rds/cluster_test.go @@ -876,6 +876,36 @@ func TestAccRDSCluster_missingUserNameCausesError(t *testing.T) { }) } +func TestAccRDSCluster_domain(t *testing.T) { + ctx := acctest.Context(t) + if testing.Short() { + t.Skip("skipping long-running test in short mode") + } + + var dbCluster rds.DBCluster + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_rds_cluster.test" + + domain := acctest.RandomDomainName() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, rds.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckClusterDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccClusterConfig_domain(rName, domain), + Check: resource.ComposeTestCheckFunc( + testAccCheckClusterExists(ctx, resourceName, &dbCluster), + resource.TestCheckResourceAttrSet(resourceName, "domain"), + resource.TestCheckResourceAttrSet(resourceName, "domain_iam_role_name"), + ), + }, + }, + }) +} + func TestAccRDSCluster_EnabledCloudWatchLogsExports_mySQL(t *testing.T) { ctx := acctest.Context(t) var dbCluster1, dbCluster2, dbCluster3 rds.DBCluster @@ -3226,6 +3256,26 @@ resource "aws_rds_cluster" "restore" { `, rName, enabledCloudwatchLogExports)) } +func testAccClusterConfig_domain(rName, domain string) string { + return acctest.ConfigCompose( + testAccConfig_ClusterSubnetGroup(rName), + testAccConfig_ServiceRole(rName), + testAccConfig_DirectoryService(rName, domain), + fmt.Sprintf(` +resource "aws_rds_cluster" "test" { + cluster_identifier = %[1]q + engine = %[2]q + master_username = "tfacctest" + master_password = "avoid-plaintext-passwords" + db_subnet_group_name = aws_db_subnet_group.test.name + skip_final_snapshot = true + domain = aws_directory_service_directory.directory.id + domain_iam_role_name = aws_iam_role.role.name +} +`, rName, tfrds.ClusterEngineAuroraPostgreSQL), + ) +} + func testAccClusterConfig_enabledCloudWatchLogsExports1(rName, enabledCloudwatchLogExports1 string) string { return fmt.Sprintf(` resource "aws_rds_cluster" "test" { @@ -4918,6 +4968,71 @@ resource "aws_db_subnet_group" "test" { ) } +func testAccConfig_ServiceRole(rName string) string { + return fmt.Sprintf(` +resource "aws_iam_role" "role" { + name = %[1]q + + assume_role_policy = < Date: Sun, 11 Feb 2024 02:36:20 +0300 Subject: [PATCH 2/8] add changelog --- .changelog/35753.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .changelog/35753.txt diff --git a/.changelog/35753.txt b/.changelog/35753.txt new file mode 100644 index 00000000000..8b12654b638 --- /dev/null +++ b/.changelog/35753.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +resource/aws_rds_cluster: Add `domain` and `domain_iam_role_name` arguments to support [Kerberos authentication](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Concepts.RDS_Fea_Regions_DB-eng.Feature.KerberosAuthentication.html) +``` \ No newline at end of file From 50e1f31b92e996f25a8d73761a65e0e00da2a172 Mon Sep 17 00:00:00 2001 From: Mehmet Gungoren Date: Sun, 11 Feb 2024 02:44:05 +0300 Subject: [PATCH 3/8] update website docs for aws_rds_cluster with domain args --- website/docs/r/rds_cluster.html.markdown | 2 ++ 1 file changed, 2 insertions(+) diff --git a/website/docs/r/rds_cluster.html.markdown b/website/docs/r/rds_cluster.html.markdown index c77fa5c4bbb..8f2cfeb675c 100644 --- a/website/docs/r/rds_cluster.html.markdown +++ b/website/docs/r/rds_cluster.html.markdown @@ -232,6 +232,8 @@ This argument supports the following arguments: * `deletion_protection` - (Optional) If the DB cluster should have deletion protection enabled. The database can't be deleted when this value is set to `true`. The default is `false`. +* `domain` - (Optional) The ID of the Directory Service Active Directory domain to create the cluster in. +* `domain_iam_role_name` - (Optional, but required if domain is provided) The name of the IAM role to be used when making API calls to the Directory Service. * `enable_global_write_forwarding` - (Optional) Whether cluster should forward writes to an associated global cluster. Applied to secondary clusters to enable them to forward writes to an [`aws_rds_global_cluster`](/docs/providers/aws/r/rds_global_cluster.html)'s primary cluster. See the [Aurora Userguide documentation](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/aurora-global-database-write-forwarding.html) for more information. * `enable_http_endpoint` - (Optional) Enable HTTP endpoint (data API). Only valid when `engine_mode` is set to `serverless`. * `enabled_cloudwatch_logs_exports` - (Optional) Set of log types to export to cloudwatch. If omitted, no logs will be exported. The following log types are supported: `audit`, `error`, `general`, `slowquery`, `postgresql` (PostgreSQL). From 7c547307bd1d8067968a42498bc88d54c494bd11 Mon Sep 17 00:00:00 2001 From: Mehmet Gungoren Date: Sun, 11 Feb 2024 03:00:50 +0300 Subject: [PATCH 4/8] add sg to cluster --- internal/service/rds/cluster_test.go | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/internal/service/rds/cluster_test.go b/internal/service/rds/cluster_test.go index 56e835f0985..c70909fea2f 100644 --- a/internal/service/rds/cluster_test.go +++ b/internal/service/rds/cluster_test.go @@ -3263,14 +3263,15 @@ func testAccClusterConfig_domain(rName, domain string) string { testAccConfig_DirectoryService(rName, domain), fmt.Sprintf(` resource "aws_rds_cluster" "test" { - cluster_identifier = %[1]q - engine = %[2]q - master_username = "tfacctest" - master_password = "avoid-plaintext-passwords" - db_subnet_group_name = aws_db_subnet_group.test.name - skip_final_snapshot = true - domain = aws_directory_service_directory.directory.id - domain_iam_role_name = aws_iam_role.role.name + cluster_identifier = %[1]q + engine = %[2]q + master_username = "tfacctest" + master_password = "avoid-plaintext-passwords" + db_subnet_group_name = aws_db_subnet_group.test.name + skip_final_snapshot = true + vpc_security_group_ids = [aws_security_group.test.id] + domain = aws_directory_service_directory.directory.id + domain_iam_role_name = aws_iam_role.role.name } `, rName, tfrds.ClusterEngineAuroraPostgreSQL), ) From fe69cc38022df7bf153765be8d81358d0a3c7fec Mon Sep 17 00:00:00 2001 From: Mehmet Gungoren Date: Sun, 11 Feb 2024 03:04:18 +0300 Subject: [PATCH 5/8] add sg to cluster --- internal/service/rds/cluster_test.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/internal/service/rds/cluster_test.go b/internal/service/rds/cluster_test.go index c70909fea2f..bdb04370db7 100644 --- a/internal/service/rds/cluster_test.go +++ b/internal/service/rds/cluster_test.go @@ -3263,15 +3263,15 @@ func testAccClusterConfig_domain(rName, domain string) string { testAccConfig_DirectoryService(rName, domain), fmt.Sprintf(` resource "aws_rds_cluster" "test" { - cluster_identifier = %[1]q - engine = %[2]q - master_username = "tfacctest" - master_password = "avoid-plaintext-passwords" - db_subnet_group_name = aws_db_subnet_group.test.name - skip_final_snapshot = true - vpc_security_group_ids = [aws_security_group.test.id] - domain = aws_directory_service_directory.directory.id - domain_iam_role_name = aws_iam_role.role.name + cluster_identifier = %[1]q + engine = %[2]q + master_username = "tfacctest" + master_password = "avoid-plaintext-passwords" + db_subnet_group_name = aws_db_subnet_group.test.name + skip_final_snapshot = true + vpc_security_group_ids = [aws_security_group.test.id] + domain = aws_directory_service_directory.directory.id + domain_iam_role_name = aws_iam_role.role.name } `, rName, tfrds.ClusterEngineAuroraPostgreSQL), ) From 239cfbdfc41eda1b28167af11c1e5885b93803c2 Mon Sep 17 00:00:00 2001 From: Mehmet Gungoren Date: Sun, 11 Feb 2024 03:09:47 +0300 Subject: [PATCH 6/8] add sg to cluster --- internal/service/rds/cluster_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/service/rds/cluster_test.go b/internal/service/rds/cluster_test.go index bdb04370db7..9c1faf694fc 100644 --- a/internal/service/rds/cluster_test.go +++ b/internal/service/rds/cluster_test.go @@ -3269,7 +3269,7 @@ resource "aws_rds_cluster" "test" { master_password = "avoid-plaintext-passwords" db_subnet_group_name = aws_db_subnet_group.test.name skip_final_snapshot = true - vpc_security_group_ids = [aws_security_group.test.id] + vpc_security_group_ids = [aws_security_group.test.id] domain = aws_directory_service_directory.directory.id domain_iam_role_name = aws_iam_role.role.name } From e65e89f91ebe8ad98d6fbefad3319d92147c633f Mon Sep 17 00:00:00 2001 From: Mehmet Gungoren Date: Sun, 11 Feb 2024 14:52:06 +0300 Subject: [PATCH 7/8] fix ad creation with two subnets --- internal/service/rds/cluster_test.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/internal/service/rds/cluster_test.go b/internal/service/rds/cluster_test.go index 9c1faf694fc..15b7c139d7a 100644 --- a/internal/service/rds/cluster_test.go +++ b/internal/service/rds/cluster_test.go @@ -4981,7 +4981,10 @@ resource "aws_iam_role" "role" { { "Action": "sts:AssumeRole", "Principal": { - "Service": "rds.${data.aws_partition.current.dns_suffix}" + "Service": [ + "directoryservice.rds.${data.aws_partition.current.dns_suffix}", + "rds.${data.aws_partition.current.dns_suffix}" + ] }, "Effect": "Allow", "Sid": "" @@ -5023,10 +5026,12 @@ resource "aws_directory_service_directory" "directory" { name = %[2]q password = "SuperSecretPassw0rd" size = "Small" + type = "MicrosoftAD" + edition = "Standard" vpc_settings { vpc_id = aws_vpc.test.id - subnet_ids = aws_subnet.test[*].id + subnet_ids = [aws_subnet.test[0].id, aws_subnet.test[1].id] } } From 3cea12a763b6292ffe632d3bc06e01469e7797f5 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Mon, 12 Feb 2024 08:46:19 -0500 Subject: [PATCH 8/8] Tweak 'TestAccRDSCluster_basic'. --- internal/service/rds/cluster_test.go | 2 ++ website/docs/r/rds_cluster.html.markdown | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/internal/service/rds/cluster_test.go b/internal/service/rds/cluster_test.go index 15b7c139d7a..fe5bb5363cf 100644 --- a/internal/service/rds/cluster_test.go +++ b/internal/service/rds/cluster_test.go @@ -82,6 +82,8 @@ func TestAccRDSCluster_basic(t *testing.T) { resource.TestCheckResourceAttrSet(resourceName, "db_cluster_parameter_group_name"), resource.TestCheckResourceAttr(resourceName, "db_system_id", ""), resource.TestCheckResourceAttr(resourceName, "delete_automated_backups", "true"), + resource.TestCheckResourceAttr(resourceName, "domain", ""), + resource.TestCheckResourceAttr(resourceName, "domain_iam_role_name", ""), resource.TestCheckResourceAttr(resourceName, "enabled_cloudwatch_logs_exports.#", "0"), resource.TestCheckResourceAttr(resourceName, "engine", tfrds.ClusterEngineAuroraMySQL), resource.TestCheckResourceAttrSet(resourceName, "engine_version"), diff --git a/website/docs/r/rds_cluster.html.markdown b/website/docs/r/rds_cluster.html.markdown index 8f2cfeb675c..a1fe8b9db17 100644 --- a/website/docs/r/rds_cluster.html.markdown +++ b/website/docs/r/rds_cluster.html.markdown @@ -233,7 +233,7 @@ This argument supports the following arguments: The database can't be deleted when this value is set to `true`. The default is `false`. * `domain` - (Optional) The ID of the Directory Service Active Directory domain to create the cluster in. -* `domain_iam_role_name` - (Optional, but required if domain is provided) The name of the IAM role to be used when making API calls to the Directory Service. +* `domain_iam_role_name` - (Optional, but required if `domain` is provided) The name of the IAM role to be used when making API calls to the Directory Service. * `enable_global_write_forwarding` - (Optional) Whether cluster should forward writes to an associated global cluster. Applied to secondary clusters to enable them to forward writes to an [`aws_rds_global_cluster`](/docs/providers/aws/r/rds_global_cluster.html)'s primary cluster. See the [Aurora Userguide documentation](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/aurora-global-database-write-forwarding.html) for more information. * `enable_http_endpoint` - (Optional) Enable HTTP endpoint (data API). Only valid when `engine_mode` is set to `serverless`. * `enabled_cloudwatch_logs_exports` - (Optional) Set of log types to export to cloudwatch. If omitted, no logs will be exported. The following log types are supported: `audit`, `error`, `general`, `slowquery`, `postgresql` (PostgreSQL).