diff --git a/.changelog/14457.txt b/.changelog/14457.txt new file mode 100644 index 00000000000..1f786a2a5ee --- /dev/null +++ b/.changelog/14457.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +resource/aws_rds_cluster: Add `enable_global_write_forwarding` attribute +``` \ No newline at end of file diff --git a/aws/resource_aws_rds_cluster.go b/aws/resource_aws_rds_cluster.go index 0c31295da32..1464748bb8e 100644 --- a/aws/resource_aws_rds_cluster.go +++ b/aws/resource_aws_rds_cluster.go @@ -131,6 +131,12 @@ func resourceAwsRDSCluster() *schema.Resource { Optional: true, }, + "enable_global_write_forwarding": { + Type: schema.TypeBool, + Optional: true, + Default: false, + }, + "reader_endpoint": { Type: schema.TypeString, Computed: true, @@ -853,6 +859,10 @@ func resourceAwsRDSClusterCreate(d *schema.ResourceData, meta interface{}) error createOpts.GlobalClusterIdentifier = aws.String(attr.(string)) } + if attr, ok := d.GetOk("enable_global_write_forwarding"); ok { + createOpts.EnableGlobalWriteForwarding = aws.Bool(attr.(bool)) + } + if attr := d.Get("vpc_security_group_ids").(*schema.Set); attr.Len() > 0 { createOpts.VpcSecurityGroupIds = expandStringSet(attr) } @@ -1208,6 +1218,11 @@ func resourceAwsRDSClusterUpdate(d *schema.ResourceData, meta interface{}) error requestUpdate = true } + if d.HasChange("enable_global_write_forwarding") { + req.EnableGlobalWriteForwarding = aws.Bool(d.Get("enable_global_write_forwarding").(bool)) + requestUpdate = true + } + if requestUpdate { err := resource.Retry(5*time.Minute, func() *resource.RetryError { _, err := conn.ModifyDBCluster(req) diff --git a/aws/resource_aws_rds_cluster_test.go b/aws/resource_aws_rds_cluster_test.go index d21c75e2841..bc81aca92b7 100644 --- a/aws/resource_aws_rds_cluster_test.go +++ b/aws/resource_aws_rds_cluster_test.go @@ -1455,6 +1455,39 @@ func TestAccAWSRDSCluster_GlobalClusterIdentifier_ReplicationSourceIdentifier(t }) } +// Reference: https://github.com/hashicorp/terraform-provider-aws/issues/14457 +func TestAccAWSRDSCluster_GlobalClusterIdentifier_SecondaryClustersWriteForwarding(t *testing.T) { + var providers []*schema.Provider + var primaryDbCluster, secondaryDbCluster rds.DBCluster + + rNameGlobal := acctest.RandomWithPrefix("tf-acc-test-global") + rNamePrimary := acctest.RandomWithPrefix("tf-acc-test-primary") + rNameSecondary := acctest.RandomWithPrefix("tf-acc-test-secondary") + + resourceNamePrimary := "aws_rds_cluster.primary" + resourceNameSecondary := "aws_rds_cluster.secondary" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + testAccMultipleRegionPreCheck(t, 2) + testAccPreCheckAWSRdsGlobalCluster(t) + }, + ProviderFactories: testAccProviderFactoriesAlternate(&providers), + CheckDestroy: testAccCheckAWSClusterDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSRDSClusterConfig_GlobalClusterIdentifier_SecondaryClustersWriteForwarding(rNameGlobal, rNamePrimary, rNameSecondary), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSClusterExistsWithProvider(resourceNamePrimary, &primaryDbCluster, testAccAwsRegionProviderFunc(testAccGetRegion(), &providers)), + testAccCheckAWSClusterExistsWithProvider(resourceNameSecondary, &secondaryDbCluster, testAccAwsRegionProviderFunc(testAccGetAlternateRegion(), &providers)), + resource.TestCheckResourceAttr(resourceNameSecondary, "enable_global_write_forwarding", "true"), + ), + }, + }, + }) +} + func TestAccAWSRDSCluster_Port(t *testing.T) { var dbCluster1, dbCluster2 rds.DBCluster rInt := acctest.RandInt() @@ -3651,6 +3684,104 @@ resource "aws_rds_cluster_instance" "secondary" { `, rNameGlobal, rNamePrimary, rNameSecondary)) } +func testAccAWSRDSClusterConfig_GlobalClusterIdentifier_SecondaryClustersWriteForwarding(rNameGlobal, rNamePrimary, rNameSecondary string) string { + return composeConfig( + testAccMultipleRegionProviderConfig(2), + fmt.Sprintf(` +data "aws_region" "current" {} + +data "aws_availability_zones" "alternate" { + provider = "awsalternate" + state = "available" + + filter { + name = "opt-in-status" + values = ["opt-in-not-required"] + } +} + +resource "aws_rds_global_cluster" "test" { + global_cluster_identifier = "%[1]s" + engine = "aurora-mysql" + engine_version = "5.7.mysql_aurora.2.08.1" +} + +resource "aws_rds_cluster" "primary" { + cluster_identifier = "%[2]s" + database_name = "mydb" + master_username = "foo" + master_password = "barbarbar" + skip_final_snapshot = true + global_cluster_identifier = aws_rds_global_cluster.test.id + engine = aws_rds_global_cluster.test.engine + engine_version = aws_rds_global_cluster.test.engine_version +} + +resource "aws_rds_cluster_instance" "primary" { + identifier = "%[2]s" + cluster_identifier = aws_rds_cluster.primary.id + instance_class = "db.r4.large" # only db.r4 or db.r5 are valid for Aurora global db + engine = aws_rds_cluster.primary.engine + engine_version = aws_rds_cluster.primary.engine_version +} + +resource "aws_vpc" "alternate" { + provider = "awsalternate" + cidr_block = "10.0.0.0/16" + + tags = { + Name = "%[3]s" + } +} + +resource "aws_subnet" "alternate" { + provider = "awsalternate" + count = 3 + vpc_id = aws_vpc.alternate.id + availability_zone = data.aws_availability_zones.alternate.names[count.index] + cidr_block = "10.0.${count.index}.0/24" + + tags = { + Name = "%[3]s" + } +} + +resource "aws_db_subnet_group" "alternate" { + provider = "awsalternate" + name = "%[3]s" + subnet_ids = aws_subnet.alternate[*].id +} + +resource "aws_rds_cluster" "secondary" { + provider = "awsalternate" + cluster_identifier = "%[3]s" + db_subnet_group_name = aws_db_subnet_group.alternate.name + skip_final_snapshot = true + source_region = data.aws_region.current.name + global_cluster_identifier = aws_rds_global_cluster.test.id + enable_global_write_forwarding = true + engine = aws_rds_global_cluster.test.engine + engine_version = aws_rds_global_cluster.test.engine_version + depends_on = [aws_rds_cluster_instance.primary] + + lifecycle { + ignore_changes = [ + replication_source_identifier, + ] + } +} + +resource "aws_rds_cluster_instance" "secondary" { + provider = "awsalternate" + identifier = "%[3]s" + cluster_identifier = aws_rds_cluster.secondary.id + instance_class = "db.r4.large" # only db.r4 or db.r5 are valid for Aurora global db + engine = aws_rds_cluster.secondary.engine + engine_version = aws_rds_cluster.secondary.engine_version +} +`, rNameGlobal, rNamePrimary, rNameSecondary)) +} + func testAccAWSRDSClusterConfig_GlobalClusterIdentifier_ReplicationSourceIdentifier(rName string) string { return composeConfig( testAccMultipleRegionProviderConfig(2), diff --git a/tools/go.sum b/tools/go.sum index 678e9fe5719..8fd0508eca6 100644 --- a/tools/go.sum +++ b/tools/go.sum @@ -420,6 +420,8 @@ github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/S github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320/go.mod h1:EiZBMaudVLy8fmjf9Npq1dq9RalhveqZG5w/yz3mHWs= github.com/hashicorp/go-getter v1.4.0/go.mod h1:7qxyCd8rBfcShwsvxgIguu4KbS3l8bUCwg2Umn7RjeY= github.com/hashicorp/go-getter v1.5.0/go.mod h1:a7z7NPPfNQpJWcn4rSWFtdrSldqLdLPEF3d8nFMsSLM= +github.com/hashicorp/go-getter v1.5.1 h1:lM9sM02nvEApQGFgkXxWbhfqtyN+AyhQmi+MaMdBDOI= +github.com/hashicorp/go-getter v1.5.1/go.mod h1:a7z7NPPfNQpJWcn4rSWFtdrSldqLdLPEF3d8nFMsSLM= github.com/hashicorp/go-getter v1.5.2 h1:XDo8LiAcDisiqZdv0TKgz+HtX3WN7zA2JD1R1tjsabE= github.com/hashicorp/go-getter v1.5.2/go.mod h1:orNH3BTYLu/fIxGIdLjLoAJHWMDQ/UKQr5O4m3iBuoo= github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd/go.mod h1:9bjs9uLqI8l75knNv3lV1kA55veR+WUPSiKIWcQHudI= diff --git a/website/docs/r/rds_cluster.html.markdown b/website/docs/r/rds_cluster.html.markdown index 0fb0c9c303b..cae276588e3 100644 --- a/website/docs/r/rds_cluster.html.markdown +++ b/website/docs/r/rds_cluster.html.markdown @@ -116,6 +116,7 @@ The following arguments are supported: * `engine` - (Optional) The name of the database engine to be used for this DB cluster. Defaults to `aurora`. Valid Values: `aurora`, `aurora-mysql`, `aurora-postgresql` * `final_snapshot_identifier` - (Optional) The name of your final DB snapshot when this DB cluster is deleted. If omitted, no final snapshot will be made. * `global_cluster_identifier` - (Optional) The global cluster identifier specified on [`aws_rds_global_cluster`](/docs/providers/aws/r/rds_global_cluster.html). +* `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. * `iam_database_authentication_enabled` - (Optional) Specifies whether or mappings of AWS Identity and Access Management (IAM) accounts to database accounts is enabled. Please see [AWS Documentation](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/UsingWithRDS.IAMDBAuth.html) for availability and limitations. * `iam_roles` - (Optional) A List of ARNs for the IAM roles to associate to the RDS Cluster. * `kms_key_id` - (Optional) The ARN for the KMS encryption key. When specifying `kms_key_id`, `storage_encrypted` needs to be set to true.