Skip to content

Commit

Permalink
Merge pull request #41314 from hashicorp/f-rds_cluster_password_wo
Browse files Browse the repository at this point in the history
r/rds_cluster: add `master_password_wo` write-only attribute
  • Loading branch information
johnsonaj authored Feb 11, 2025
2 parents 35debe3 + 517c477 commit 21898f1
Show file tree
Hide file tree
Showing 4 changed files with 110 additions and 3 deletions.
3 changes: 3 additions & 0 deletions .changelog/41314.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:enhancement
resource/aws_rds_cluster: Add `master_password_wo` write-only attribute
```
58 changes: 56 additions & 2 deletions internal/service/rds/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"github.com/aws/aws-sdk-go-v2/service/rds"
"github.com/aws/aws-sdk-go-v2/service/rds/types"
"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"
Expand Down Expand Up @@ -68,6 +69,10 @@ func resourceCluster() *schema.Resource {
},
},

ValidateRawResourceConfigFuncs: []schema.ValidateRawResourceConfigFunc{
validation.PreferWriteOnlyAttribute(cty.GetAttrPath("master_password"), cty.GetAttrPath("master_password_wo")),
},

Schema: map[string]*schema.Schema{
names.AttrAllocatedStorage: {
Type: schema.TypeInt,
Expand Down Expand Up @@ -303,7 +308,7 @@ func resourceCluster() *schema.Resource {
"manage_master_user_password": {
Type: schema.TypeBool,
Optional: true,
ConflictsWith: []string{"master_password"},
ConflictsWith: []string{"master_password", "master_password_wo"},
},
"master_user_secret": {
Type: schema.TypeList,
Expand Down Expand Up @@ -335,7 +340,19 @@ func resourceCluster() *schema.Resource {
Type: schema.TypeString,
Optional: true,
Sensitive: true,
ConflictsWith: []string{"manage_master_user_password"},
ConflictsWith: []string{"manage_master_user_password", "master_password_wo"},
},
"master_password_wo": {
Type: schema.TypeString,
Optional: true,
Sensitive: true,
WriteOnly: true,
ConflictsWith: []string{"manage_master_user_password", "master_password"},
},
"master_password_wo_version": {
Type: schema.TypeInt,
Optional: true,
RequiredWith: []string{"master_password_wo"},
},
"master_username": {
Type: schema.TypeString,
Expand Down Expand Up @@ -671,6 +688,13 @@ func resourceClusterCreate(ctx context.Context, d *schema.ResourceData, meta int
create.WithDefaultPrefix("tf-"),
).Generate()

// get write-only value from configuration
masterPasswordWO, di := flex.GetWriteOnlyStringValue(d, cty.GetAttrPath("master_password_wo"))
diags = append(diags, di...)
if diags.HasError() {
return diags
}

// Some API calls (e.g. RestoreDBClusterFromSnapshot do not support all
// parameters to correctly apply all settings in one pass. For missing
// parameters or unsupported configurations, we may need to call
Expand Down Expand Up @@ -751,6 +775,11 @@ func resourceClusterCreate(ctx context.Context, d *schema.ResourceData, meta int
requiresModifyDbCluster = true
}

if masterPasswordWO != "" {
modifyDbClusterInput.MasterUserPassword = aws.String(masterPasswordWO)
requiresModifyDbCluster = true
}

if v, ok := d.GetOk("master_user_secret_kms_key_id"); ok {
modifyDbClusterInput.MasterUserSecretKmsKeyId = aws.String(v.(string))
requiresModifyDbCluster = true
Expand Down Expand Up @@ -899,6 +928,10 @@ func resourceClusterCreate(ctx context.Context, d *schema.ResourceData, meta int
input.MasterUserPassword = aws.String(v.(string))
}

if masterPasswordWO != "" {
input.MasterUserPassword = aws.String(masterPasswordWO)
}

if v, ok := d.GetOk("network_type"); ok {
input.NetworkType = aws.String(v.(string))
}
Expand Down Expand Up @@ -1009,6 +1042,11 @@ func resourceClusterCreate(ctx context.Context, d *schema.ResourceData, meta int
requiresModifyDbCluster = true
}

if masterPasswordWO != "" {
modifyDbClusterInput.MasterUserPassword = aws.String(masterPasswordWO)
requiresModifyDbCluster = true
}

if v, ok := d.GetOk("master_user_secret_kms_key_id"); ok {
modifyDbClusterInput.MasterUserSecretKmsKeyId = aws.String(v.(string))
requiresModifyDbCluster = true
Expand Down Expand Up @@ -1199,6 +1237,10 @@ func resourceClusterCreate(ctx context.Context, d *schema.ResourceData, meta int
input.MasterUserPassword = aws.String(v.(string))
}

if masterPasswordWO != "" {
input.MasterUserPassword = aws.String(masterPasswordWO)
}

if v, ok := d.GetOk("master_user_secret_kms_key_id"); ok {
input.MasterUserSecretKmsKeyId = aws.String(v.(string))
}
Expand Down Expand Up @@ -1599,6 +1641,18 @@ func resourceClusterUpdate(ctx context.Context, d *schema.ResourceData, meta int
}
}

if d.HasChange("master_password_wo_version") {
masterPasswordWO, di := flex.GetWriteOnlyStringValue(d, cty.GetAttrPath("master_password_wo"))
diags = append(diags, di...)
if diags.HasError() {
return diags
}

if masterPasswordWO != "" {
input.MasterUserPassword = aws.String(masterPasswordWO)
}
}

if d.HasChange("master_user_secret_kms_key_id") {
if v, ok := d.GetOk("master_user_secret_kms_key_id"); ok {
input.MasterUserSecretKmsKeyId = aws.String(v.(string))
Expand Down
48 changes: 48 additions & 0 deletions internal/service/rds/cluster_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,12 @@ import (
"github.com/aws/aws-sdk-go-v2/service/rds"
"github.com/aws/aws-sdk-go-v2/service/rds/types"
"github.com/hashicorp/aws-sdk-go-base/v2/endpoints"
"github.com/hashicorp/go-version"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest"
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
"github.com/hashicorp/terraform-plugin-testing/terraform"
"github.com/hashicorp/terraform-plugin-testing/tfversion"
"github.com/hashicorp/terraform-provider-aws/internal/acctest"
"github.com/hashicorp/terraform-provider-aws/internal/conns"
tfrds "github.com/hashicorp/terraform-provider-aws/internal/service/rds"
Expand All @@ -45,6 +47,7 @@ func testAccClusterImportStep(n string) resource.TestStep {
"enable_local_write_forwarding",
"manage_master_user_password",
"master_password",
"master_password_wo",
"master_user_secret_kms_key_id",
"skip_final_snapshot",
},
Expand Down Expand Up @@ -2952,6 +2955,37 @@ func TestAccRDSCluster_password(t *testing.T) {
})
}

func TestAccRDSCluster_passwordWriteOnly(t *testing.T) {
ctx := acctest.Context(t)
var dbCluster types.DBCluster
rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)
resourceName := "aws_rds_cluster.test"

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(ctx, t) },
ErrorCheck: acctest.ErrorCheck(t, names.RDSServiceID),
TerraformVersionChecks: []tfversion.TerraformVersionCheck{
tfversion.SkipBelow(version.Must(version.NewVersion("1.11.0"))),
},
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories,
CheckDestroy: testAccCheckClusterDestroy(ctx),
Steps: []resource.TestStep{
{
Config: testAccClusterConfig_passwordWriteOnly(rName, "valid-password-1", 1),
Check: resource.ComposeAggregateTestCheckFunc(
testAccCheckClusterExists(ctx, resourceName, &dbCluster),
),
},
{
Config: testAccClusterConfig_passwordWriteOnly(rName, "valid-password-2", 2),
Check: resource.ComposeAggregateTestCheckFunc(
testAccCheckClusterExists(ctx, resourceName, &dbCluster),
),
},
},
})
}

func TestAccRDSCluster_NoDeleteAutomatedBackups(t *testing.T) {
ctx := acctest.Context(t)
var dbCluster types.DBCluster
Expand Down Expand Up @@ -6119,6 +6153,20 @@ resource "aws_rds_cluster" "test" {
`, rName, password, tfrds.ClusterEngineAuroraMySQL)
}

func testAccClusterConfig_passwordWriteOnly(rName, password string, passwordVersion int) string {
return fmt.Sprintf(`
resource "aws_rds_cluster" "test" {
cluster_identifier = %[1]q
database_name = "test"
master_username = "tfacctest"
master_password_wo = %[2]q
master_password_wo_version = %[4]d
engine = %[3]q
skip_final_snapshot = true
}
`, rName, password, tfrds.ClusterEngineAuroraMySQL, passwordVersion)
}

func testAccConfig_ClusterSubnetGroup(rName string) string {
return acctest.ConfigCompose(
acctest.ConfigVPCWithSubnets(rName, 3),
Expand Down
4 changes: 3 additions & 1 deletion website/docs/r/rds_cluster.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,9 @@ This resource supports the following arguments:
* `iops` - (Optional) Amount of Provisioned IOPS (input/output operations per second) to be initially allocated for each DB instance in the Multi-AZ DB cluster. For information about valid Iops values, see [Amazon RDS Provisioned IOPS storage to improve performance](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/CHAP_Storage.html#USER_PIOPS) in the Amazon RDS User Guide. (This setting is required to create a Multi-AZ DB cluster). Must be a multiple between .5 and 50 of the storage amount for the DB cluster.
* `kms_key_id` - (Optional) ARN for the KMS encryption key. When specifying `kms_key_id`, `storage_encrypted` needs to be set to true.
* `manage_master_user_password` - (Optional) Set to true to allow RDS to manage the master user password in Secrets Manager. Cannot be set if `master_password` is provided.
* `master_password` - (Required unless `manage_master_user_password` is set to true or unless a `snapshot_identifier` or `replication_source_identifier` is provided or unless a `global_cluster_identifier` is provided when the cluster is the "secondary" cluster of a global database) Password for the master DB user. Note that this may show up in logs, and it will be stored in the state file. Please refer to the [RDS Naming Constraints][5]. Cannot be set if `manage_master_user_password` is set to `true`.
* `master_password` - (Optional, required unless `manage_master_user_password` is set to true, a `snapshot_identifier`, `replication_source_identifier`, or `master_password_wo` is provided or unless a `global_cluster_identifier` is provided when the cluster is the "secondary" cluster of a global database) Password for the master DB user. Note that this may show up in logs, and it will be stored in the state file. Please refer to the [RDS Naming Constraints][5]. Cannot be set if `manage_master_user_password` is set to `true`.
* `master_password_wo` (Optional, Write-Only required unless `manage_master_user_password` is set to true, a `snapshot_identifier`, `replication_source_identifier`, or `master_password` is provided or unless a `global_cluster_identifier` is provided when the cluster is the "secondary" cluster of a global database) Password for the master DB user. Note that this may show up in logs. Please refer to the [RDS Naming Constraints][5]. Cannot be set if `manage_master_user_password` is set to `true`.
* `master_password_wo_version` - (Optional) Used together with `master_password_wo` to trigger an update. Increment this value when an update to the `master_password_wo` is required.
* `master_user_secret_kms_key_id` - (Optional) Amazon Web Services KMS key identifier is the key ARN, key ID, alias ARN, or alias name for the KMS key. To use a KMS key in a different Amazon Web Services account, specify the key ARN or alias ARN. If not specified, the default KMS key for your Amazon Web Services account is used.
* `master_username` - (Required unless a `snapshot_identifier` or `replication_source_identifier` is provided or unless a `global_cluster_identifier` is provided when the cluster is the "secondary" cluster of a global database) Username for the master DB user. Please refer to the [RDS Naming Constraints][5]. This argument does not support in-place updates and cannot be changed during a restore from snapshot.
* `monitoring_interval` - (Optional) Interval, in seconds, in seconds, between points when Enhanced Monitoring metrics are collected for the DB cluster. To turn off collecting Enhanced Monitoring metrics, specify 0. The default is 0. Valid Values: 0, 1, 5, 10, 15, 30, 60.
Expand Down

0 comments on commit 21898f1

Please sign in to comment.