Skip to content

Commit

Permalink
Merge pull request #35753 from gungoren/f-aws_rds_cluster-add-kerbero…
Browse files Browse the repository at this point in the history
…s-domain-auth

r/aws_rds_cluster: add domain and domain_iam_role_name to rds_cluster
  • Loading branch information
ewbankkit authored Feb 12, 2024
2 parents 63a0f5a + 3cea12a commit af4efa6
Show file tree
Hide file tree
Showing 4 changed files with 181 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .changelog/35753.txt
Original file line number Diff line number Diff line change
@@ -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)
```
53 changes: 53 additions & 0 deletions internal/service/rds/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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))
}
Expand Down Expand Up @@ -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))
}
Expand Down Expand Up @@ -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))
}
Expand Down Expand Up @@ -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))
}
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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))
}
Expand Down
123 changes: 123 additions & 0 deletions internal/service/rds/cluster_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"),
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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" {
Expand Down Expand Up @@ -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 = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Principal": {
"Service": [
"directoryservice.rds.${data.aws_partition.current.dns_suffix}",
"rds.${data.aws_partition.current.dns_suffix}"
]
},
"Effect": "Allow",
"Sid": ""
}
]
}
EOF
}
resource "aws_iam_role_policy_attachment" "attatch-policy" {
role = aws_iam_role.role.name
policy_arn = "arn:${data.aws_partition.current.partition}:iam::aws:policy/service-role/AmazonRDSDirectoryServiceAccess"
}
`, rName)
}

func testAccConfig_DirectoryService(rName, domain string) string {
return fmt.Sprintf(`
resource "aws_security_group" "test" {
name = %[1]q
vpc_id = aws_vpc.test.id
tags = {
Name = %[1]q
}
}
resource "aws_security_group_rule" "test" {
type = "egress"
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
security_group_id = aws_security_group.test.id
}
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[0].id, aws_subnet.test[1].id]
}
}
data "aws_partition" "current" {}
`, rName, domain)
}

func testAccClusterConfig_noDeleteAutomatedBackups(rName, preferredBackupWindow string) string {
return fmt.Sprintf(`
resource "aws_rds_cluster" "test" {
Expand Down
2 changes: 2 additions & 0 deletions website/docs/r/rds_cluster.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand Down

0 comments on commit af4efa6

Please sign in to comment.