Skip to content

Commit

Permalink
Merge pull request #40428 from hashicorp/d-document-rds-parameter-gro…
Browse files Browse the repository at this point in the history
…up-diffs

docs: Better document perpetual diffs
  • Loading branch information
YakDriver authored Dec 4, 2024
2 parents 5afb09a + e611c7f commit 66b416d
Show file tree
Hide file tree
Showing 2 changed files with 151 additions and 19 deletions.
87 changes: 73 additions & 14 deletions internal/service/rds/parameter_group_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,7 @@ func TestAccRDSParameterGroup_basic(t *testing.T) {
Config: testAccParameterGroupConfig_basic(rName),
Check: resource.ComposeTestCheckFunc(
testAccCheckParameterGroupExists(ctx, resourceName, &v),
testAccCheckParameterGroupAttributes(&v, rName),
testAccCheckParameterGroupAttributes(&v, rName, "mysql5.6"),
resource.TestCheckResourceAttr(resourceName, names.AttrName, rName),
resource.TestCheckResourceAttr(resourceName, names.AttrFamily, "mysql5.6"),
resource.TestCheckTypeSetElemNestedAttrs(resourceName, "parameter.*", map[string]string{
Expand All @@ -295,7 +295,7 @@ func TestAccRDSParameterGroup_basic(t *testing.T) {
Config: testAccParameterGroupConfig_addParameters(rName),
Check: resource.ComposeTestCheckFunc(
testAccCheckParameterGroupExists(ctx, resourceName, &v),
testAccCheckParameterGroupAttributes(&v, rName),
testAccCheckParameterGroupAttributes(&v, rName, "mysql5.6"),
resource.TestCheckResourceAttr(resourceName, names.AttrName, rName),
resource.TestCheckResourceAttr(resourceName, names.AttrFamily, "mysql5.6"),
resource.TestCheckTypeSetElemNestedAttrs(resourceName, "parameter.*", map[string]string{
Expand Down Expand Up @@ -325,7 +325,7 @@ func TestAccRDSParameterGroup_basic(t *testing.T) {
Config: testAccParameterGroupConfig_basic(rName),
Check: resource.ComposeTestCheckFunc(
testAccCheckParameterGroupExists(ctx, resourceName, &v),
testAccCheckParameterGroupAttributes(&v, rName),
testAccCheckParameterGroupAttributes(&v, rName, "mysql5.6"),
testAccCheckParameterNotUserDefined(ctx, resourceName, "collation_connection"),
testAccCheckParameterNotUserDefined(ctx, resourceName, "collation_server"),
resource.TestCheckResourceAttr(resourceName, "parameter.#", "3"),
Expand Down Expand Up @@ -451,7 +451,7 @@ func TestAccRDSParameterGroup_limit(t *testing.T) {
Config: testAccParameterGroupConfig_exceedDefaultLimit(rName),
Check: resource.ComposeTestCheckFunc(
testAccCheckParameterGroupExists(ctx, resourceName, &v),
testAccCheckParameterGroupAttributes(&v, rName),
testAccCheckParameterGroupAttributes(&v, rName, "mysql5.6"),
resource.TestCheckResourceAttr(resourceName, names.AttrName, rName),
resource.TestCheckResourceAttr(resourceName, names.AttrFamily, "mysql5.6"),
resource.TestCheckResourceAttr(resourceName, names.AttrDescription, "RDS default parameter group: Exceed default AWS parameter group limit of twenty"),
Expand Down Expand Up @@ -630,7 +630,7 @@ func TestAccRDSParameterGroup_limit(t *testing.T) {
Config: testAccParameterGroupConfig_updateExceedDefaultLimit(rName),
Check: resource.ComposeTestCheckFunc(
testAccCheckParameterGroupExists(ctx, resourceName, &v),
testAccCheckParameterGroupAttributes(&v, rName),
testAccCheckParameterGroupAttributes(&v, rName, "mysql5.6"),
resource.TestCheckResourceAttr(resourceName, names.AttrName, rName),
resource.TestCheckResourceAttr(resourceName, names.AttrFamily, "mysql5.6"),
resource.TestCheckResourceAttr(resourceName, names.AttrDescription, "Updated RDS default parameter group: Exceed default AWS parameter group limit of twenty"),
Expand Down Expand Up @@ -861,7 +861,7 @@ func TestAccRDSParameterGroup_withApplyMethod(t *testing.T) {
Config: testAccParameterGroupConfig_applyMethod(rName),
Check: resource.ComposeTestCheckFunc(
testAccCheckParameterGroupExists(ctx, resourceName, &v),
testAccCheckParameterGroupAttributes(&v, rName),
testAccCheckParameterGroupAttributes(&v, rName, "mysql5.6"),
resource.TestCheckResourceAttr(resourceName, names.AttrName, rName),
resource.TestCheckResourceAttr(resourceName, names.AttrFamily, "mysql5.6"),
resource.TestCheckResourceAttr(resourceName, names.AttrDescription, "Managed by Terraform"),
Expand Down Expand Up @@ -902,7 +902,7 @@ func TestAccRDSParameterGroup_only(t *testing.T) {
Config: testAccParameterGroupConfig_only(rName),
Check: resource.ComposeTestCheckFunc(
testAccCheckParameterGroupExists(ctx, resourceName, &v),
testAccCheckParameterGroupAttributes(&v, rName),
testAccCheckParameterGroupAttributes(&v, rName, "mysql5.6"),
resource.TestCheckResourceAttr(resourceName, names.AttrName, rName),
resource.TestCheckResourceAttr(resourceName, names.AttrFamily, "mysql5.6"),
),
Expand Down Expand Up @@ -962,7 +962,7 @@ func TestAccRDSParameterGroup_updateParameters(t *testing.T) {
Config: testAccParameterGroupConfig_updateParametersInitial(rName),
Check: resource.ComposeTestCheckFunc(
testAccCheckParameterGroupExists(ctx, resourceName, &v),
testAccCheckParameterGroupAttributes(&v, rName),
testAccCheckParameterGroupAttributes(&v, rName, "mysql5.6"),
resource.TestCheckResourceAttr(resourceName, names.AttrName, rName),
resource.TestCheckResourceAttr(resourceName, names.AttrFamily, "mysql5.6"),
resource.TestCheckTypeSetElemNestedAttrs(resourceName, "parameter.*", map[string]string{
Expand All @@ -988,7 +988,7 @@ func TestAccRDSParameterGroup_updateParameters(t *testing.T) {
Config: testAccParameterGroupConfig_updateParametersUpdated(rName),
Check: resource.ComposeTestCheckFunc(
testAccCheckParameterGroupExists(ctx, resourceName, &v),
testAccCheckParameterGroupAttributes(&v, rName),
testAccCheckParameterGroupAttributes(&v, rName, "mysql5.6"),
resource.TestCheckTypeSetElemNestedAttrs(resourceName, "parameter.*", map[string]string{
names.AttrName: "character_set_results",
names.AttrValue: "ascii",
Expand All @@ -1007,6 +1007,51 @@ func TestAccRDSParameterGroup_updateParameters(t *testing.T) {
})
}

func TestAccRDSParameterGroup_updateParameters2(t *testing.T) {
ctx := acctest.Context(t)
var v types.DBParameterGroup
resourceName := "aws_db_parameter_group.test"
rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)
fam := "mysql5.7"

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(ctx, t) },
ErrorCheck: acctest.ErrorCheck(t, names.RDSServiceID),
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories,
CheckDestroy: testAccCheckParameterGroupDestroy(ctx),
Steps: []resource.TestStep{
{
Config: testAccParameterGroupConfig_updateParameters(rName, fam, "pending-reboot", "default_password_lifetime", "0"),
Check: resource.ComposeTestCheckFunc(
testAccCheckParameterGroupExists(ctx, resourceName, &v),
testAccCheckParameterGroupAttributes(&v, rName, fam),
resource.TestCheckResourceAttr(resourceName, names.AttrName, rName),
resource.TestCheckResourceAttr(resourceName, names.AttrFamily, fam),
resource.TestCheckTypeSetElemNestedAttrs(resourceName, "parameter.*", map[string]string{
"apply_method": "pending-reboot",
names.AttrName: "default_password_lifetime",
names.AttrValue: "0",
}),
),
},
{
Config: testAccParameterGroupConfig_updateParameters(rName, fam, "immediate", "default_password_lifetime", "1"),
Check: resource.ComposeTestCheckFunc(
testAccCheckParameterGroupExists(ctx, resourceName, &v),
testAccCheckParameterGroupAttributes(&v, rName, fam),
resource.TestCheckResourceAttr(resourceName, names.AttrName, rName),
resource.TestCheckResourceAttr(resourceName, names.AttrFamily, fam),
resource.TestCheckTypeSetElemNestedAttrs(resourceName, "parameter.*", map[string]string{
"apply_method": "immediate",
names.AttrName: "default_password_lifetime",
names.AttrValue: "1",
}),
),
},
},
})
}

func TestAccRDSParameterGroup_caseParameters(t *testing.T) {
ctx := acctest.Context(t)
var v types.DBParameterGroup
Expand All @@ -1023,7 +1068,7 @@ func TestAccRDSParameterGroup_caseParameters(t *testing.T) {
Config: testAccParameterGroupConfig_upperCase(rName, "Max_connections"),
Check: resource.ComposeTestCheckFunc(
testAccCheckParameterGroupExists(ctx, resourceName, &v),
testAccCheckParameterGroupAttributes(&v, rName),
testAccCheckParameterGroupAttributes(&v, rName, "mysql5.6"),
resource.TestCheckResourceAttr(resourceName, names.AttrName, rName),
resource.TestCheckResourceAttr(resourceName, names.AttrFamily, "mysql5.6"),
resource.TestCheckTypeSetElemNestedAttrs(resourceName, "parameter.*", map[string]string{
Expand Down Expand Up @@ -1112,15 +1157,14 @@ func testAccCheckParameterGroupNoDestroy(ctx context.Context) resource.TestCheck
}
}

func testAccCheckParameterGroupAttributes(v *types.DBParameterGroup, name string) resource.TestCheckFunc {
func testAccCheckParameterGroupAttributes(v *types.DBParameterGroup, name, fam string) resource.TestCheckFunc {
return func(s *terraform.State) error {
if *v.DBParameterGroupName != name {
return fmt.Errorf("Bad Parameter Group name, expected (%s), got (%s)", name, *v.DBParameterGroupName)
}

family := "mysql5.6"
if aws.ToString(v.DBParameterGroupFamily) != family {
return fmt.Errorf("bad family, got: %s, expecting: %s", aws.ToString(v.DBParameterGroupFamily), family)
if aws.ToString(v.DBParameterGroupFamily) != fam {
return fmt.Errorf("bad family, got: %s, expecting: %s", aws.ToString(v.DBParameterGroupFamily), fam)
}

return nil
Expand Down Expand Up @@ -1883,6 +1927,21 @@ resource "aws_db_parameter_group" "test" {
`, rName)
}

func testAccParameterGroupConfig_updateParameters(rName, family, apply, param, value string) string {
return fmt.Sprintf(`
resource "aws_db_parameter_group" "test" {
name = %[1]q
family = %[2]q
parameter {
apply_method = %[3]q
name = %[4]q
value = %[5]s
}
}
`, rName, family, apply, param, value)
}

func testAccParameterGroupConfig_upperCase(rName, paramName string) string {
return fmt.Sprintf(`
resource "aws_db_parameter_group" "test" {
Expand Down
83 changes: 78 additions & 5 deletions website/docs/r/db_parameter_group.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,7 @@ Provides an RDS DB parameter group resource. Documentation of the available para

> **Hands-on:** For an example of the `aws_db_parameter_group` in use, follow the [Manage AWS RDS Instances](https://learn.hashicorp.com/tutorials/terraform/aws-rds?in=terraform/aws&utm_source=WEBSITE&utm_medium=WEB_IO&utm_offer=ARTICLE_PAGE&utm_content=DOCS) tutorial on HashiCorp Learn.
~> **NOTE:** After applying your changes, you may encounter a perpetual diff in your Terraform plan
output for a `parameter` whose `value` remains unchanged but whose `apply_method` is changing
(e.g., from `immediate` to `pending-reboot`, or `pending-reboot` to `immediate`). If only the
apply method of a parameter is changing, the AWS API will not register this change. To change
the `apply_method` of a parameter, its value must also change.
~> **NOTE:** If you encounter a Terraform plan showing parameter changes after an apply (_i.e._, _perpetual diffs_), see the [Problematic Plan Changes](#problematic-plan-changes) example below for additional guidance.

## Example Usage

Expand Down Expand Up @@ -75,6 +71,83 @@ resource "aws_db_instance" "example" {
}
```

### Problematic Plan Changes

If you are experiencing unexpected `update in-place` plan changes after running `terraform apply` (_i.e._, "perpetual diffs"), it is likely due to conflicts between the AWS Provider's default behavior and AWS's requirements for managing parameter groups. The following characteristics of parameter management are relevant:

1. The AWS Provider's default `apply_method` is `immediate`.
2. AWS automatically assigns default parameters with predefined values and `apply_method` settings when you create a parameter group.
3. AWS does not allow changing the `apply_method` of a default parameter (or an existing parameter) without also modifying its `value`. For example, you cannot change the `apply_method` from `pending-reboot` to `immediate` or vice versa without adjusting the parameter's value.

See an example of this type of problem and solutions below.

#### Example of Problematic Configuration

The following Terraform configuration includes a parameter that overlaps with an AWS default parameter, using the same `name` (`default_password_lifetime`) and `value` (`0`). However:

- AWS sets the default `apply_method` for this parameter to `pending-reboot`.
- The AWS Provider defaults all parameters' `apply_method` to `immediate`.

This configuration attempts to change _only_ the `apply_method` from `pending-reboot` to `immediate`, which is not allowed by AWS.

```terraform
resource "aws_db_parameter_group" "test" {
name = "random-test-parameter"
family = "mysql5.7"
parameter {
# By default, the apply_method is being set to "immediate"
name = "default_password_lifetime" # same as AWS default
value = "0" # same as AWS default
}
}
```

#### Solution 1: Remove the Default Parameter

Exclude the default parameter, such as `default_password_lifetime` in this example, from your configuration entirely. This ensures Terraform does not attempt to modify the parameter, leaving it with AWS's default settings.

```terraform
resource "aws_db_parameter_group" "test" {
name = "random-test-parameter"
family = "mysql5.7"
}
```

#### Solution 2: Modify the Parameter's Value Also

Change the `value` of the parameter along with its `apply_method`. Since the AWS default `value` is `0`, selecting any other valid value (_e.g._, `1`) will resolve the issue.

```terraform
resource "aws_db_parameter_group" "test" {
name = "random-test-parameter"
family = "mysql5.7"
parameter {
# Because of the default, the apply_method will also be changed from `pending-reboot` to `immediate`
name = "default_password_lifetime" # same as AWS default
value = "1" # different from AWS default, "0"
}
}
```

#### Solution 3: Align `apply_method` with AWS Defaults

Explicitly set the `apply_method` to match AWS's default value for this parameter (`pending-reboot`). This prevents conflicts between Terraform's default (`immediate`) and AWS's default where the `value` is not changing.

```terraform
resource "aws_db_parameter_group" "test" {
name = "random-test-parameter"
family = "mysql5.7"
parameter {
apply_method = "pending-reboot" # same as AWS default
name = "default_password_lifetime" # same as AWS default
value = "0" # same as AWS default
}
}
```

## Argument Reference

This resource supports the following arguments:
Expand Down

0 comments on commit 66b416d

Please sign in to comment.