diff --git a/.changelog/45049.txt b/.changelog/45049.txt new file mode 100644 index 000000000000..68e418ab23fc --- /dev/null +++ b/.changelog/45049.txt @@ -0,0 +1,3 @@ +```release-note:bug +resource/aws_docdb_cluster: Allow adding and modifying `serverless_v2_scaling_configuration` without forcing cluster replacement +``` diff --git a/internal/service/docdb/cluster.go b/internal/service/docdb/cluster.go index b672d280ece3..ae2290f35147 100644 --- a/internal/service/docdb/cluster.go +++ b/internal/service/docdb/cluster.go @@ -17,7 +17,6 @@ import ( "github.com/hashicorp/aws-sdk-go-base/v2/tfawserr" "github.com/hashicorp/go-cty/cty" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/customdiff" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" @@ -303,9 +302,10 @@ func resourceCluster() *schema.Resource { }, }, "serverless_v2_scaling_configuration": { - Type: schema.TypeList, - Optional: true, - MaxItems: 1, + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + DiffSuppressFunc: verify.SuppressMissingOptionalConfigurationBlock, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ names.AttrMaxCapacity: { @@ -315,6 +315,15 @@ func resourceCluster() *schema.Resource { validation.FloatBetween(1.0, 256.0), validateServerlessCapacity, ), + DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { + config := d.GetRawConfig() + raw := config.GetAttr("serverless_v2_scaling_configuration") + + if raw.LengthInt() == 0 && (old != "0" && old != "") && (new == "0" || new == "") { + return true + } + return false + }, }, "min_capacity": { Type: schema.TypeFloat, @@ -323,6 +332,15 @@ func resourceCluster() *schema.Resource { validation.FloatBetween(0.5, 256.0), validateServerlessCapacity, ), + DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { + config := d.GetRawConfig() + raw := config.GetAttr("serverless_v2_scaling_configuration") + + if raw.LengthInt() == 0 && (old != "0" && old != "") && (new == "0" || new == "") { + return true + } + return false + }, }, }, }, @@ -372,18 +390,6 @@ func resourceCluster() *schema.Resource { Elem: &schema.Schema{Type: schema.TypeString}, }, }, - CustomizeDiff: customdiff.All( - // if serverless_v2_scaling_configuration is newly set or deleted, ForceNew is required - customdiff.ForceNewIfChange("serverless_v2_scaling_configuration", - func(_ context.Context, old, new, meta any) bool { - o := old != nil && len(old.([]any)) > 0 - n := new != nil && len(new.([]any)) > 0 - if (o && n) || (!o && !n) { - return false - } - return true - }), - ), } } diff --git a/internal/service/docdb/cluster_test.go b/internal/service/docdb/cluster_test.go index 8656f91de0c3..636af3879319 100644 --- a/internal/service/docdb/cluster_test.go +++ b/internal/service/docdb/cluster_test.go @@ -1085,10 +1085,43 @@ func TestAccDocDBCluster_serverless(t *testing.T) { ), }, { + // Remove serverless config from Terraform code + // DiffSuppressFunc should prevent drift warning Config: testAccClusterConfig_serverlessRemoved(rName), ConfigPlanChecks: resource.ConfigPlanChecks{ PreApply: []plancheck.PlanCheck{ - plancheck.ExpectResourceAction(resourceName, plancheck.ResourceActionDestroyBeforeCreate), + plancheck.ExpectResourceAction(resourceName, plancheck.ResourceActionNoop), + }, + }, + Check: resource.ComposeTestCheckFunc( + testAccCheckClusterExists(ctx, resourceName, &dbCluster), + // AWS still has the serverless config, and DiffSuppressFunc accepts it + resource.TestCheckResourceAttr(resourceName, "serverless_v2_scaling_configuration.#", "1"), + resource.TestCheckResourceAttr(resourceName, "serverless_v2_scaling_configuration.0.min_capacity", "1"), + resource.TestCheckResourceAttr(resourceName, "serverless_v2_scaling_configuration.0.max_capacity", "1.5"), + ), + }, + }, + }) +} + +func TestAccDocDBCluster_serverlessAddToExisting(t *testing.T) { + ctx := acctest.Context(t) + var dbCluster awstypes.DBCluster + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_docdb_cluster.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.DocDBServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckClusterDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccClusterConfig_serverlessRemoved(rName), + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(resourceName, plancheck.ResourceActionCreate), }, }, Check: resource.ComposeTestCheckFunc( @@ -1096,6 +1129,32 @@ func TestAccDocDBCluster_serverless(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "serverless_v2_scaling_configuration.#", "0"), ), }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + names.AttrAllowMajorVersionUpgrade, + names.AttrApplyImmediately, + names.AttrFinalSnapshotIdentifier, + "master_password", + "skip_final_snapshot", + }, + }, + { + Config: testAccClusterConfig_serverless(rName, 0.5, 4.0), + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(resourceName, plancheck.ResourceActionUpdate), + }, + }, + Check: resource.ComposeTestCheckFunc( + testAccCheckClusterExists(ctx, resourceName, &dbCluster), + resource.TestCheckResourceAttr(resourceName, "serverless_v2_scaling_configuration.#", "1"), + resource.TestCheckResourceAttr(resourceName, "serverless_v2_scaling_configuration.0.min_capacity", "0.5"), + resource.TestCheckResourceAttr(resourceName, "serverless_v2_scaling_configuration.0.max_capacity", "4"), + ), + }, }, }) } diff --git a/website/docs/r/docdb_cluster.html.markdown b/website/docs/r/docdb_cluster.html.markdown index 6661eb06a7a1..0793cb7b6490 100644 --- a/website/docs/r/docdb_cluster.html.markdown +++ b/website/docs/r/docdb_cluster.html.markdown @@ -100,7 +100,8 @@ The `restore_to_point_in_time` block supports the following arguments: ### Serverless V2 Scaling Configuration The `serverless_v2_scaling_configuration` block supports the following arguments. -Adding this block (i.e. switching to serverless) or removing it (i.e. switching from serverless) will trigger cluster replacement. + +~> **NOTE:** Once configured, the serverless scaling configuration cannot be removed without recreating the cluster. If the configuration block is removed from Terraform, the configuration values will remain and continue to be active. * `max_capacity` - (Required) Maximum number of Amazon DocumentDB capacity units (DCUs) for an instance in an Amazon DocumentDB Serverless cluster. Valid values are multiples of 0.5 between 1 and 256. * `min_capacity` - (Required) Minimum number of Amazon DocumentDB capacity units (DCUs) for an instance in an Amazon DocumentDB Serverless cluster. Valid values are multiples of 0.5 between 0.5 and 256.