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 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..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"), @@ -876,6 +878,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 +3258,27 @@ 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 + 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), + ) +} + func testAccClusterConfig_enabledCloudWatchLogsExports1(rName, enabledCloudwatchLogExports1 string) string { return fmt.Sprintf(` resource "aws_rds_cluster" "test" { @@ -4918,6 +4971,76 @@ 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 = <