Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add authentication_mode to aws_elasticache_user #28928

Merged
merged 14 commits into from
Mar 15, 2023
3 changes: 3 additions & 0 deletions .changelog/28928.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:enhancement
resource/aws_elasticache_user: Add `authentication_mode` argument
```
56 changes: 54 additions & 2 deletions internal/service/elasticache/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,34 @@ func ResourceUser() *schema.Resource {
Optional: true,
Computed: true,
},
"authentication_mode": {
Type: schema.TypeList,
Optional: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"passwords": {
Type: schema.TypeSet,
Optional: true,
MinItems: 1,
Set: schema.HashString,
Sensitive: true,
Elem: &schema.Schema{
Type: schema.TypeString,
},
},
"password_count": {
Type: schema.TypeInt,
Computed: true,
},
"type": {
Type: schema.TypeString,
Required: true,
ValidateFunc: validation.StringInSlice(elasticache.InputAuthenticationType_Values(), false),
ewbankkit marked this conversation as resolved.
Show resolved Hide resolved
},
},
},
},
"engine": {
Type: schema.TypeString,
Required: true,
Expand Down Expand Up @@ -83,7 +111,11 @@ func resourceUserCreate(d *schema.ResourceData, meta interface{}) error {
tags := defaultTagsConfig.MergeTags(tftags.New(d.Get("tags").(map[string]interface{})))

input := &elasticache.CreateUserInput{
AccessString: aws.String(d.Get("access_string").(string)),
AccessString: aws.String(d.Get("access_string").(string)),
AuthenticationMode: &elasticache.AuthenticationMode{
Passwords: flex.ExpandStringSet(d.Get("authentication_mode.0.passwords").(*schema.Set)),
Type: aws.String(d.Get("authentication_mode.0.type").(string)),
},
Engine: aws.String(d.Get("engine").(string)),
NoPasswordRequired: aws.Bool(d.Get("no_password_required").(bool)),
UserId: aws.String(d.Get("user_id").(string)),
Expand Down Expand Up @@ -147,10 +179,23 @@ func resourceUserRead(d *schema.ResourceData, meta interface{}) error {
}

d.Set("access_string", resp.AccessString)
d.Set("arn", resp.ARN)

if v := resp.Authentication; v != nil {
authenticationMode := map[string]interface{}{
"passwords": d.Get("authentication_mode.0.passwords"),
"password_count": aws.Int64Value(v.PasswordCount),
"type": aws.StringValue(v.Type),
}

if err := d.Set("authentication_mode", []interface{}{authenticationMode}); err != nil {
return fmt.Errorf("failed to set authentication_mode of ElastiCache User (%s): %s", d.Id(), err)
}
}

d.Set("engine", resp.Engine)
d.Set("user_id", resp.UserId)
d.Set("user_name", resp.UserName)
d.Set("arn", resp.ARN)

tags, err := ListTags(conn, aws.StringValue(resp.ARN))

Expand Down Expand Up @@ -193,6 +238,13 @@ func resourceUserUpdate(d *schema.ResourceData, meta interface{}) error {
hasChange = true
}

if d.HasChange("authentication_mode") {
req.AuthenticationMode = &elasticache.AuthenticationMode{
Passwords: flex.ExpandStringSet(d.Get("authentication_mode.0.passwords").(*schema.Set)),
Type: aws.String(d.Get("authentication_mode.0.type").(string)),
}
}

if d.HasChange("no_password_required") {
req.NoPasswordRequired = aws.Bool(d.Get("no_password_required").(bool))
hasChange = true
Expand Down
189 changes: 189 additions & 0 deletions internal/service/elasticache/user_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,99 @@ func TestAccElastiCacheUser_basic(t *testing.T) {
})
}

func TestAccElastiCacheUserWithPasswordAuthMode_basic(t *testing.T) {
var user elasticache.User
rName := sdkacctest.RandomWithPrefix("tf-acc")
resourceName := "aws_elasticache_user.test"

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(t) },
ErrorCheck: acctest.ErrorCheck(t, elasticache.EndpointsID),
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories,
CheckDestroy: testAccCheckUserDestroy,
Steps: []resource.TestStep{
{
Config: testAccUserConfigWithPasswordAuthMode_basic(rName),
Check: resource.ComposeTestCheckFunc(
testAccCheckUserExists(resourceName, &user),
resource.TestCheckResourceAttr(resourceName, "user_id", rName),
resource.TestCheckResourceAttr(resourceName, "user_name", "username1"),
resource.TestCheckResourceAttr(resourceName, "engine", "redis"),
resource.TestCheckResourceAttr(resourceName, "authentication_mode.0.password_count", "1"),
resource.TestCheckResourceAttr(resourceName, "authentication_mode.0.passwords.#", "1"),
resource.TestCheckTypeSetElemAttr(resourceName, "authentication_mode.0.passwords.*", "aaaaaaaaaaaaaaaa"),
resource.TestCheckResourceAttr(resourceName, "authentication_mode.0.type", "password"),
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}

func TestAccElastiCacheUserWithIamAuthMode_basic(t *testing.T) {
var user elasticache.User
rName := sdkacctest.RandomWithPrefix("tf-acc")
resourceName := "aws_elasticache_user.test"

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(t) },
ErrorCheck: acctest.ErrorCheck(t, elasticache.EndpointsID),
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories,
CheckDestroy: testAccCheckUserDestroy,
Steps: []resource.TestStep{
{
Config: testAccUserConfigWithIamAuthMode_basic(rName),
Check: resource.ComposeTestCheckFunc(
testAccCheckUserExists(resourceName, &user),
resource.TestCheckResourceAttr(resourceName, "user_id", rName),
resource.TestCheckResourceAttr(resourceName, "user_name", "username1"),
resource.TestCheckResourceAttr(resourceName, "engine", "redis"),
resource.TestCheckResourceAttr(resourceName, "authentication_mode.0.type", "iam"),
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}

func TestAccElastiCacheUserWithNoPassRequiredAuthMode_basic(t *testing.T) {
var user elasticache.User
rName := sdkacctest.RandomWithPrefix("tf-acc")
resourceName := "aws_elasticache_user.test"

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(t) },
ErrorCheck: acctest.ErrorCheck(t, elasticache.EndpointsID),
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories,
CheckDestroy: testAccCheckUserDestroy,
Steps: []resource.TestStep{
{
Config: testAccUserConfigWithNoPassRequiredAuthMode_basic(rName),
Check: resource.ComposeTestCheckFunc(
testAccCheckUserExists(resourceName, &user),
resource.TestCheckResourceAttr(resourceName, "user_id", rName),
resource.TestCheckResourceAttr(resourceName, "user_name", "username1"),
resource.TestCheckResourceAttr(resourceName, "engine", "redis"),
resource.TestCheckResourceAttr(resourceName, "authentication_mode.0.type", "no-password-required"),
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}

func TestAccElastiCacheUser_update(t *testing.T) {
var user elasticache.User
rName := sdkacctest.RandomWithPrefix("tf-acc")
Expand Down Expand Up @@ -87,6 +180,40 @@ func TestAccElastiCacheUser_update(t *testing.T) {
})
}

func TestAccElastiCacheUserWithPasswordAuthMode_update_password(t *testing.T) {
var user elasticache.User
rName := sdkacctest.RandomWithPrefix("tf-acc")
resourceName := "aws_elasticache_user.test"

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(t) },
ErrorCheck: acctest.ErrorCheck(t, elasticache.EndpointsID),
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories,
CheckDestroy: testAccCheckUserDestroy,
Steps: []resource.TestStep{
{
Config: testAccUserConfigWithPasswordAuthMode_basic(rName),
Check: resource.ComposeTestCheckFunc(
testAccCheckUserExists(resourceName, &user),
resource.TestCheckResourceAttr(resourceName, "authentication_mode.0.password_count", "1"),
),
},
{
Config: testAccUserConfigWithPasswordAuthMode_update(rName, "bbbbbbbbbbbbbbbb", "cccccccccccc"),
Check: resource.ComposeTestCheckFunc(
testAccCheckUserExists(resourceName, &user),
resource.TestCheckResourceAttr(resourceName, "authentication_mode.0.password_count", "2"),
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}

func TestAccElastiCacheUser_tags(t *testing.T) {
var user elasticache.User
rName := sdkacctest.RandomWithPrefix("tf-acc")
Expand Down Expand Up @@ -230,6 +357,52 @@ resource "aws_elasticache_user" "test" {
`, rName))
}

func testAccUserConfigWithPasswordAuthMode_basic(rName string) string {
return acctest.ConfigCompose(acctest.ConfigAvailableAZsNoOptIn(), fmt.Sprintf(`
resource "aws_elasticache_user" "test" {
user_id = %[1]q
user_name = "username1"
access_string = "on ~app::* -@all +@read +@hash +@bitmap +@geo -setbit -bitfield -hset -hsetnx -hmset -hincrby -hincrbyfloat -hdel -bitop -geoadd -georadius -georadiusbymember"
engine = "REDIS"

authentication_mode {
type = "password"
passwords = ["aaaaaaaaaaaaaaaa"]
}
}
`, rName))
}

func testAccUserConfigWithIamAuthMode_basic(rName string) string {
return acctest.ConfigCompose(acctest.ConfigAvailableAZsNoOptIn(), fmt.Sprintf(`
resource "aws_elasticache_user" "test" {
user_id = %[1]q
user_name = "username1"
access_string = "on ~app::* -@all +@read +@hash +@bitmap +@geo -setbit -bitfield -hset -hsetnx -hmset -hincrby -hincrbyfloat -hdel -bitop -geoadd -georadius -georadiusbymember"
engine = "REDIS"

authentication_mode {
type = "iam"
}
}
`, rName))
}

func testAccUserConfigWithNoPassRequiredAuthMode_basic(rName string) string {
return acctest.ConfigCompose(acctest.ConfigAvailableAZsNoOptIn(), fmt.Sprintf(`
resource "aws_elasticache_user" "test" {
user_id = %[1]q
user_name = "username1"
access_string = "on ~app::* -@all +@read +@hash +@bitmap +@geo -setbit -bitfield -hset -hsetnx -hmset -hincrby -hincrbyfloat -hdel -bitop -geoadd -georadius -georadiusbymember"
engine = "REDIS"

authentication_mode {
type = "no-password-required"
}
}
`, rName))
}

func testAccUserConfig_update(rName string) string {
return acctest.ConfigCompose(acctest.ConfigAvailableAZsNoOptIn(), fmt.Sprintf(`
resource "aws_elasticache_user" "test" {
Expand All @@ -242,6 +415,22 @@ resource "aws_elasticache_user" "test" {
`, rName))
}

func testAccUserConfigWithPasswordAuthMode_update(rName string, password1 string, password2 string) string {
return acctest.ConfigCompose(acctest.ConfigAvailableAZsNoOptIn(), fmt.Sprintf(`
resource "aws_elasticache_user" "test" {
user_id = %[1]q
user_name = "username1"
access_string = "on ~app::* -@all +@read +@hash +@bitmap +@geo -setbit -bitfield -hset -hsetnx -hmset -hincrby -hincrbyfloat -hdel -bitop -geoadd -georadius -georadiusbymember"
engine = "REDIS"

authentication_mode {
type = "password"
passwords = [%[2]q, %[3]q]
}
}
`, rName, password1, password2))
}

func testAccUserConfig_tags(rName, tagKey, tagValue string) string {
return acctest.ConfigCompose(acctest.ConfigAvailableAZsNoOptIn(), fmt.Sprintf(`
resource "aws_elasticache_user" "test" {
Expand Down