diff --git a/.changelog/25704.txt b/.changelog/25704.txt new file mode 100644 index 00000000000..1188f578d54 --- /dev/null +++ b/.changelog/25704.txt @@ -0,0 +1,3 @@ +```release-note:new-data-source +aws_secretsmanager_random_password +``` \ No newline at end of file diff --git a/internal/provider/provider.go b/internal/provider/provider.go index d84aca949b6..6baee4e386e 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -846,6 +846,7 @@ func Provider() *schema.Provider { "aws_sagemaker_prebuilt_ecr_image": sagemaker.DataSourcePrebuiltECRImage(), + "aws_secretsmanager_random_password": secretsmanager.DataSourceRandomPassword(), "aws_secretsmanager_secret": secretsmanager.DataSourceSecret(), "aws_secretsmanager_secret_rotation": secretsmanager.DataSourceSecretRotation(), "aws_secretsmanager_secret_version": secretsmanager.DataSourceSecretVersion(), diff --git a/internal/service/secretsmanager/random_password_data_source.go b/internal/service/secretsmanager/random_password_data_source.go new file mode 100644 index 00000000000..5ba7611a3bd --- /dev/null +++ b/internal/service/secretsmanager/random_password_data_source.go @@ -0,0 +1,117 @@ +package secretsmanager + +import ( + "fmt" + "log" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/secretsmanager" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-provider-aws/internal/conns" +) + +func DataSourceRandomPassword() *schema.Resource { + return &schema.Resource{ + Read: dataSourceRandomPasswordRead, + + Schema: map[string]*schema.Schema{ + "exclude_characters": { + Type: schema.TypeString, + Optional: true, + }, + "exclude_lowercase": { + Type: schema.TypeBool, + Optional: true, + }, + "exclude_numbers": { + Type: schema.TypeBool, + Optional: true, + }, + "exclude_punctuation": { + Type: schema.TypeBool, + Optional: true, + }, + "exclude_uppercase": { + Type: schema.TypeBool, + Optional: true, + }, + "include_space": { + Type: schema.TypeBool, + Optional: true, + }, + "password_length": { + Type: schema.TypeInt, + Optional: true, + Default: 32, + }, + "require_each_included_type": { + Type: schema.TypeBool, + Optional: true, + }, + "random_password": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + }, + } +} + +func dataSourceRandomPasswordRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*conns.AWSClient).SecretsManagerConn + + var excludeCharacters string + if v, ok := d.GetOk("exclude_characters"); ok { + excludeCharacters = v.(string) + } + var excludeLowercase bool + if v, ok := d.GetOk("exclude_lowercase"); ok { + excludeLowercase = v.(bool) + } + var excludeNumbers bool + if v, ok := d.GetOk("exclude_numbers"); ok { + excludeNumbers = v.(bool) + } + var excludePunctuation bool + if v, ok := d.GetOk("exclude_punctuation"); ok { + excludePunctuation = v.(bool) + } + var excludeUppercase bool + if v, ok := d.GetOk("exclude_uppercase"); ok { + excludeUppercase = v.(bool) + } + var includeSpace bool + if v, ok := d.GetOk("exclude_space"); ok { + includeSpace = v.(bool) + } + var passwordLength int64 + if v, ok := d.GetOk("password_length"); ok { + passwordLength = int64(v.(int)) + } + var requireEachIncludedType bool + if v, ok := d.GetOk("require_each_included_type"); ok { + requireEachIncludedType = v.(bool) + } + + input := &secretsmanager.GetRandomPasswordInput{ + ExcludeCharacters: aws.String(excludeCharacters), + ExcludeLowercase: aws.Bool(excludeLowercase), + ExcludeNumbers: aws.Bool(excludeNumbers), + ExcludePunctuation: aws.Bool(excludePunctuation), + ExcludeUppercase: aws.Bool(excludeUppercase), + IncludeSpace: aws.Bool(includeSpace), + PasswordLength: aws.Int64(passwordLength), + RequireEachIncludedType: aws.Bool(requireEachIncludedType), + } + + log.Printf("[DEBUG] Reading Secrets Manager Get Random Password: %s", input) + output, err := conn.GetRandomPassword(input) + if err != nil { + return fmt.Errorf("error reading Secrets Manager Get Random Password: %w", err) + } + + d.SetId(aws.StringValue(output.RandomPassword)) + d.Set("random_password", output.RandomPassword) + + return nil +} diff --git a/internal/service/secretsmanager/random_password_data_source_test.go b/internal/service/secretsmanager/random_password_data_source_test.go new file mode 100644 index 00000000000..d7e9eab13ae --- /dev/null +++ b/internal/service/secretsmanager/random_password_data_source_test.go @@ -0,0 +1,107 @@ +package secretsmanager_test + +import ( + "fmt" + "testing" + "unicode" + + "github.com/aws/aws-sdk-go/service/secretsmanager" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "github.com/hashicorp/terraform-provider-aws/internal/acctest" +) + +func TestAccSecretsManagerRandomPasswordDataSource_basic(t *testing.T) { + datasourceName := "data.aws_secretsmanager_random_password.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t); testAccPreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, secretsmanager.EndpointsID), + ProviderFactories: acctest.ProviderFactories, + Steps: []resource.TestStep{ + { + Config: testAccRandomPasswordDataSourceConfig_basic(), + Check: resource.ComposeTestCheckFunc( + testAccRandomPasswordDataSource(datasourceName, 40), + ), + }, + }, + }) +} + +func TestAccSecretsManagerRandomPasswordDataSource_exclude(t *testing.T) { + datasourceName := "data.aws_secretsmanager_random_password.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t); testAccPreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, secretsmanager.EndpointsID), + ProviderFactories: acctest.ProviderFactories, + Steps: []resource.TestStep{ + { + Config: testAccRandomPasswordDataSourceConfig_exclude(), + Check: resource.ComposeTestCheckFunc( + func(s *terraform.State) error { + dataSource, ok := s.RootModule().Resources[datasourceName] + if !ok { + return fmt.Errorf("root module has no resource called %s", datasourceName) + } + + if len(dataSource.Primary.Attributes["random_password"]) != 40 { + return fmt.Errorf( + "len(%s) != %d", + dataSource.Primary.Attributes["random_password"], + 40, + ) + } + + for _, r := range dataSource.Primary.Attributes["random_password"] { + if !(unicode.IsLower(r) && unicode.IsLetter(r)) { + return fmt.Errorf("expected only lowercase letters") + } + } + + return nil + }, + ), + }, + }, + }) +} + +func testAccRandomPasswordDataSource(datasourceName string, expectedLength int) resource.TestCheckFunc { + return func(s *terraform.State) error { + dataSource, ok := s.RootModule().Resources[datasourceName] + if !ok { + return fmt.Errorf("root module has no resource called %s", datasourceName) + } + + if len(dataSource.Primary.Attributes["random_password"]) != expectedLength { + return fmt.Errorf( + "len(%s) != %d", + dataSource.Primary.Attributes["random_password"], + expectedLength, + ) + } + + return nil + } +} + +func testAccRandomPasswordDataSourceConfig_basic() string { + return ` +data "aws_secretsmanager_random_password" "test" { + password_length = 40 +} +` +} + +func testAccRandomPasswordDataSourceConfig_exclude() string { + return ` +data "aws_secretsmanager_random_password" "test" { + password_length = 40 + exclude_numbers = true + exclude_uppercase = true + exclude_punctuation = true +} +` +} diff --git a/website/docs/d/secretsmanager_random_password.html.markdown b/website/docs/d/secretsmanager_random_password.html.markdown new file mode 100644 index 00000000000..4d2877bdd36 --- /dev/null +++ b/website/docs/d/secretsmanager_random_password.html.markdown @@ -0,0 +1,35 @@ +--- +subcategory: "Secrets Manager" +layout: "aws" +page_title: "AWS: aws_secretsmanager_random_password" +description: |- + Generate a random password +--- + +# Data Source: aws_secretsmanager_random_password + +Generate a random password. + +## Example Usage + +```terraform +data "aws_secretsmanager_random_password" "test" { + password_length = 50 + exclude_numbers = true +} +``` + +## Argument Reference + +* `exclude_characters` - (Optional) A string of the characters that you don't want in the password. +* `exclude_lowercase` - (Optional) Specifies whether to exclude lowercase letters from the password. +* `exclude_numbers` - (Optional) Specifies whether to exclude numbers from the password. +* `exclude_punctuation` - (Optional) Specifies whether to exclude the following punctuation characters from the password: ``! " # $ % & ' ( ) * + , - . / : ; < = > ? @ [ \ ] ^ _ ` { | } ~ .`` +* `exclude_uppercase` - (Optional) Specifies whether to exclude uppercase letters from the password. +* `include_space` - (Optional) Specifies whether to include the space character. +* `password_length` - (Optional) The length of the password. +* `require_each_included_type` - (Optional) Specifies whether to include at least one upper and lowercase letter, one number, and one punctuation. + +## Attributes Reference + +* `random_password` - The random password.