diff --git a/.changelog/35965.txt b/.changelog/35965.txt new file mode 100644 index 00000000000..61ca9139a81 --- /dev/null +++ b/.changelog/35965.txt @@ -0,0 +1,8 @@ +```release-note:enhancement +resource/aws_redshiftserverless_namespace: +Add attributes `admin_password_secret_kms_key_id` and `manage_admin_password` +``` + +```release-note:bug +data/aws_redshiftserverless_namespace: Properly set `iam_roles` attribute on read +``` diff --git a/internal/service/redshiftserverless/namespace.go b/internal/service/redshiftserverless/namespace.go index d3830440e1d..a7466403088 100644 --- a/internal/service/redshiftserverless/namespace.go +++ b/internal/service/redshiftserverless/namespace.go @@ -40,10 +40,21 @@ func ResourceNamespace() *schema.Resource { }, Schema: map[string]*schema.Schema{ + "admin_password_secret_arn": { + Type: schema.TypeString, + Computed: true, + }, + "admin_password_secret_kms_key_id": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateFunc: verify.ValidKMSKeyID, + }, "admin_user_password": { - Type: schema.TypeString, - Optional: true, - Sensitive: true, + Type: schema.TypeString, + Optional: true, + Sensitive: true, + ConflictsWith: []string{"manage_admin_password"}, }, "admin_username": { Type: schema.TypeString, @@ -89,6 +100,11 @@ func ResourceNamespace() *schema.Resource { ValidateFunc: validation.StringInSlice(redshiftserverless.LogExport_Values(), false), }, }, + "manage_admin_password": { + Type: schema.TypeBool, + Optional: true, + ConflictsWith: []string{"admin_user_password"}, + }, "namespace_id": { Type: schema.TypeString, Computed: true, @@ -116,6 +132,10 @@ func resourceNamespaceCreate(ctx context.Context, d *schema.ResourceData, meta i Tags: getTagsIn(ctx), } + if v, ok := d.GetOk("admin_password_secret_kms_key_id"); ok { + input.AdminPasswordSecretKmsKeyId = aws.String(v.(string)) + } + if v, ok := d.GetOk("admin_user_password"); ok { input.AdminUserPassword = aws.String(v.(string)) } @@ -144,6 +164,10 @@ func resourceNamespaceCreate(ctx context.Context, d *schema.ResourceData, meta i input.LogExports = flex.ExpandStringSet(v.(*schema.Set)) } + if v, ok := d.GetOk("manage_admin_password"); ok { + input.ManageAdminPassword = aws.Bool(v.(bool)) + } + output, err := conn.CreateNamespaceWithContext(ctx, input) if err != nil { @@ -172,6 +196,8 @@ func resourceNamespaceRead(ctx context.Context, d *schema.ResourceData, meta int } arn := aws.StringValue(output.NamespaceArn) + d.Set("admin_password_secret_arn", output.AdminPasswordSecretArn) + d.Set("admin_password_secret_kms_key_id", output.AdminPasswordSecretKmsKeyId) d.Set("admin_username", output.AdminUsername) d.Set("arn", arn) d.Set("db_name", output.DbName) @@ -194,6 +220,10 @@ func resourceNamespaceUpdate(ctx context.Context, d *schema.ResourceData, meta i NamespaceName: aws.String(d.Id()), } + if d.HasChanges("admin_password_secret_kms_key_id") { + input.AdminPasswordSecretKmsKeyId = aws.String(d.Get("admin_password_secret_kms_key_id").(string)) + } + if d.HasChanges("admin_username", "admin_user_password") { input.AdminUsername = aws.String(d.Get("admin_username").(string)) input.AdminUserPassword = aws.String(d.Get("admin_user_password").(string)) @@ -215,6 +245,10 @@ func resourceNamespaceUpdate(ctx context.Context, d *schema.ResourceData, meta i input.LogExports = flex.ExpandStringSet(d.Get("log_exports").(*schema.Set)) } + if d.HasChange("manage_admin_password") { + input.ManageAdminPassword = aws.Bool(d.Get("manage_admin_password").(bool)) + } + _, err := conn.UpdateNamespaceWithContext(ctx, input) if err != nil { diff --git a/internal/service/redshiftserverless/namespace_data_source.go b/internal/service/redshiftserverless/namespace_data_source.go index 3ddf6e3481d..fec51b3fd1e 100644 --- a/internal/service/redshiftserverless/namespace_data_source.go +++ b/internal/service/redshiftserverless/namespace_data_source.go @@ -82,7 +82,7 @@ func dataSourceNamespaceRead(ctx context.Context, d *schema.ResourceData, meta i d.Set("arn", resource.NamespaceArn) d.Set("db_name", resource.DbName) d.Set("default_iam_role_arn", resource.DefaultIamRoleArn) - d.Set("iam_roles", resource.IamRoles) + d.Set("iam_roles", flattenNamespaceIAMRoles(resource.IamRoles)) d.Set("kms_key_id", resource.KmsKeyId) d.Set("log_exports", resource.LogExports) diff --git a/internal/service/redshiftserverless/namespace_test.go b/internal/service/redshiftserverless/namespace_test.go index 9bc299cfcce..aab254da169 100644 --- a/internal/service/redshiftserverless/namespace_test.go +++ b/internal/service/redshiftserverless/namespace_test.go @@ -218,6 +218,30 @@ func TestAccRedshiftServerlessNamespace_withWorkgroup(t *testing.T) { }) } +func TestAccRedshiftServerlessNamespace_manageAdminPassword(t *testing.T) { + ctx := acctest.Context(t) + resourceName := "aws_redshiftserverless_namespace.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.RedshiftServerlessServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckNamespaceDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccNamespaceConfig_manageAdminPassword(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckNamespaceExists(ctx, resourceName), + testAccCheckNamespaceExists(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, "manage_admin_password", "true"), + resource.TestCheckResourceAttrSet(resourceName, "admin_password_secret_arn"), + ), + }, + }, + }) +} + func testAccCheckNamespaceDestroy(ctx context.Context) resource.TestCheckFunc { return func(s *terraform.State) error { conn := acctest.Provider.Meta().(*conns.AWSClient).RedshiftServerlessConn(ctx) @@ -363,6 +387,15 @@ resource "aws_redshiftserverless_namespace" "test" { `, rName)) } +func testAccNamespaceConfig_manageAdminPassword(rName string) string { + return fmt.Sprintf(` +resource "aws_redshiftserverless_namespace" "test" { + namespace_name = %[1]q + manage_admin_password = true +} +`, rName) +} + func testAccNamespaceConfig_withWorkgroup(rName string) string { return acctest.ConfigCompose(testAccNamespaceConfig_baseIAMRole(rName, 2), fmt.Sprintf(` resource "aws_redshiftserverless_namespace" "test" { diff --git a/website/docs/r/redshiftserverless_namespace.html.markdown b/website/docs/r/redshiftserverless_namespace.html.markdown index e2b6e628efd..6c3fef8645b 100644 --- a/website/docs/r/redshiftserverless_namespace.html.markdown +++ b/website/docs/r/redshiftserverless_namespace.html.markdown @@ -22,7 +22,9 @@ resource "aws_redshiftserverless_namespace" "example" { This resource supports the following arguments: +* `admin_password_secret_kms_key_id` - (Optional) ID of the KMS key used to encrypt the namespace's admin credentials secret. * `admin_user_password` - (Optional) The password of the administrator for the first database created in the namespace. + Conflicts with `manage_admin_password`. * `admin_username` - (Optional) The username of the administrator for the first database created in the namespace. * `db_name` - (Optional) The name of the first database created in the namespace. * `default_iam_role_arn` - (Optional) The Amazon Resource Name (ARN) of the IAM role to set as a default in the namespace. When specifying `default_iam_role_arn`, it also must be part of `iam_roles`. @@ -30,6 +32,8 @@ This resource supports the following arguments: * `kms_key_id` - (Optional) The ARN of the Amazon Web Services Key Management Service key used to encrypt your data. * `log_exports` - (Optional) The types of logs the namespace can export. Available export types are `userlog`, `connectionlog`, and `useractivitylog`. * `namespace_name` - (Required) The name of the namespace. +* `manage_admin_password` - (Optional) Whether to use AWS SecretManager to manage namespace's admin credentials. + Conflicts with `admin_user_password`. * `tags` - (Optional) A map of tags to assign to the resource. If configured with a provider [`default_tags` configuration block](https://registry.terraform.io/providers/hashicorp/aws/latest/docs#default_tags-configuration-block) present, tags with matching keys will overwrite those defined at the provider-level. ## Attribute Reference