diff --git a/.changelog/23765.txt b/.changelog/23765.txt new file mode 100644 index 00000000000..4cf518e0321 --- /dev/null +++ b/.changelog/23765.txt @@ -0,0 +1,3 @@ +```release-note:new-resource +aws_cognito_user_in_group +``` diff --git a/internal/provider/provider.go b/internal/provider/provider.go index 72cd598284d..d938cd5d763 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -1081,8 +1081,9 @@ func Provider() *schema.Provider { "aws_cognito_identity_provider": cognitoidp.ResourceIdentityProvider(), "aws_cognito_resource_server": cognitoidp.ResourceResourceServer(), - "aws_cognito_user_group": cognitoidp.ResourceUserGroup(), "aws_cognito_user": cognitoidp.ResourceUser(), + "aws_cognito_user_group": cognitoidp.ResourceUserGroup(), + "aws_cognito_user_in_group": cognitoidp.ResourceUserInGroup(), "aws_cognito_user_pool": cognitoidp.ResourceUserPool(), "aws_cognito_user_pool_client": cognitoidp.ResourceUserPoolClient(), "aws_cognito_user_pool_domain": cognitoidp.ResourceUserPoolDomain(), diff --git a/internal/service/cognitoidp/find.go b/internal/service/cognitoidp/find.go index 60a89ce1762..1d5f833b872 100644 --- a/internal/service/cognitoidp/find.go +++ b/internal/service/cognitoidp/find.go @@ -1,6 +1,7 @@ package cognitoidp import ( + "fmt" "reflect" "github.com/aws/aws-sdk-go/aws" @@ -33,3 +34,42 @@ func FindCognitoUserPoolUICustomization(conn *cognitoidentityprovider.CognitoIde return output.UICustomization, nil } + +// FindCognitoUserInGroup checks whether the specified user is present in the specified group. Returns boolean value accordingly. +func FindCognitoUserInGroup(conn *cognitoidentityprovider.CognitoIdentityProvider, groupName, userPoolId, username string) (bool, error) { + input := &cognitoidentityprovider.AdminListGroupsForUserInput{ + UserPoolId: aws.String(userPoolId), + Username: aws.String(username), + } + + found := false + + err := conn.AdminListGroupsForUserPages(input, func(page *cognitoidentityprovider.AdminListGroupsForUserOutput, lastPage bool) bool { + if page == nil { + return !lastPage + } + + for _, group := range page.Groups { + if group == nil { + continue + } + + if aws.StringValue(group.GroupName) == groupName { + found = true + break + } + } + + if found { + return false + } + + return !lastPage + }) + + if err != nil { + return false, fmt.Errorf("error reading groups for user: %w", err) + } + + return found, nil +} diff --git a/internal/service/cognitoidp/user_in_group.go b/internal/service/cognitoidp/user_in_group.go new file mode 100644 index 00000000000..916993a01ad --- /dev/null +++ b/internal/service/cognitoidp/user_in_group.go @@ -0,0 +1,111 @@ +package cognitoidp + +import ( + "fmt" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/cognitoidentityprovider" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + "github.com/hashicorp/terraform-provider-aws/internal/conns" +) + +func ResourceUserInGroup() *schema.Resource { + return &schema.Resource{ + Create: resourceUserInGroupCreate, + Read: resourceUserInGroupRead, + Delete: resourceUserInGroupDelete, + Schema: map[string]*schema.Schema{ + "group_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validUserGroupName, + }, + "user_pool_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validUserPoolID, + }, + "username": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringLenBetween(1, 128), + }, + }, + } +} + +func resourceUserInGroupCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*conns.AWSClient).CognitoIDPConn + + input := &cognitoidentityprovider.AdminAddUserToGroupInput{} + + if v, ok := d.GetOk("group_name"); ok { + input.GroupName = aws.String(v.(string)) + } + + if v, ok := d.GetOk("user_pool_id"); ok { + input.UserPoolId = aws.String(v.(string)) + } + + if v, ok := d.GetOk("username"); ok { + input.Username = aws.String(v.(string)) + } + + _, err := conn.AdminAddUserToGroup(input) + + if err != nil { + return fmt.Errorf("error adding user to group: %w", err) + } + + //lintignore:R015 // Allow legacy unstable ID usage in managed resource + d.SetId(resource.UniqueId()) + + return resourceUserInGroupRead(d, meta) +} + +func resourceUserInGroupRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*conns.AWSClient).CognitoIDPConn + + groupName := d.Get("group_name").(string) + userPoolId := d.Get("user_pool_id").(string) + username := d.Get("username").(string) + + found, err := FindCognitoUserInGroup(conn, groupName, userPoolId, username) + + if err != nil { + return err + } + + if !found { + d.SetId("") + } + + return nil +} + +func resourceUserInGroupDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*conns.AWSClient).CognitoIDPConn + + groupName := d.Get("group_name").(string) + userPoolID := d.Get("user_pool_id").(string) + username := d.Get("username").(string) + + input := &cognitoidentityprovider.AdminRemoveUserFromGroupInput{ + GroupName: aws.String(groupName), + UserPoolId: aws.String(userPoolID), + Username: aws.String(username), + } + + _, err := conn.AdminRemoveUserFromGroup(input) + + if err != nil { + return fmt.Errorf("error removing user from group: %w", err) + } + + return nil +} diff --git a/internal/service/cognitoidp/user_in_group_test.go b/internal/service/cognitoidp/user_in_group_test.go new file mode 100644 index 00000000000..c71de673674 --- /dev/null +++ b/internal/service/cognitoidp/user_in_group_test.go @@ -0,0 +1,152 @@ +package cognitoidp_test + +import ( + "errors" + "fmt" + "testing" + + "github.com/aws/aws-sdk-go/service/cognitoidentityprovider" + "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" + sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "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" + "github.com/hashicorp/terraform-provider-aws/internal/conns" + tfcognitoidp "github.com/hashicorp/terraform-provider-aws/internal/service/cognitoidp" +) + +func TestAccCognitoUserInGroup_basic(t *testing.T) { + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_cognito_user_in_group.test" + userPoolResourceName := "aws_cognito_user_pool.test" + userGroupResourceName := "aws_cognito_user_group.test" + userResourceName := "aws_cognito_user.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, cognitoidentityprovider.EndpointsID), + Providers: acctest.Providers, + CheckDestroy: testAccCheckUserInGroupDestroy, + Steps: []resource.TestStep{ + { + Config: testAccConfigUserInGroup_basic(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckUserInGroupExists(resourceName), + resource.TestCheckResourceAttrPair(resourceName, "user_pool_id", userPoolResourceName, "id"), + resource.TestCheckResourceAttrPair(resourceName, "group_name", userGroupResourceName, "name"), + resource.TestCheckResourceAttrPair(resourceName, "username", userResourceName, "username"), + ), + }, + }, + }) +} + +func TestAccCognitoUserInGroup_disappears(t *testing.T) { + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_cognito_user_in_group.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, cognitoidentityprovider.EndpointsID), + Providers: acctest.Providers, + CheckDestroy: testAccCheckUserInGroupDestroy, + Steps: []resource.TestStep{ + { + Config: testAccConfigUserInGroup_basic(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckUserInGroupExists(resourceName), + acctest.CheckResourceDisappears(acctest.Provider, tfcognitoidp.ResourceUserInGroup(), resourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func testAccConfigUserInGroup_basic(rName string) string { + return fmt.Sprintf(` +resource "aws_cognito_user_pool" "test" { + name = %[1]q + password_policy { + temporary_password_validity_days = 7 + minimum_length = 6 + require_uppercase = false + require_symbols = false + require_numbers = false + } +} + +resource "aws_cognito_user" "test" { + user_pool_id = aws_cognito_user_pool.test.id + username = %[1]q +} + +resource "aws_cognito_user_group" "test" { + user_pool_id = aws_cognito_user_pool.test.id + name = %[1]q +} + +resource "aws_cognito_user_in_group" "test" { + user_pool_id = aws_cognito_user_pool.test.id + group_name = aws_cognito_user_group.test.name + username = aws_cognito_user.test.username +} +`, rName) +} + +func testAccCheckUserInGroupExists(resourceName string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return fmt.Errorf("resource not found: %s", resourceName) + } + + conn := acctest.Provider.Meta().(*conns.AWSClient).CognitoIDPConn + + groupName := rs.Primary.Attributes["group_name"] + userPoolId := rs.Primary.Attributes["user_pool_id"] + username := rs.Primary.Attributes["username"] + + found, err := tfcognitoidp.FindCognitoUserInGroup(conn, groupName, userPoolId, username) + + if err != nil { + return err + } + + if !found { + return errors.New("user in group not found") + } + + return nil + } +} + +func testAccCheckUserInGroupDestroy(s *terraform.State) error { + conn := acctest.Provider.Meta().(*conns.AWSClient).CognitoIDPConn + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_cognito_user_in_group" { + continue + } + + groupName := rs.Primary.Attributes["group_name"] + userPoolId := rs.Primary.Attributes["user_pool_id"] + username := rs.Primary.Attributes["username"] + + found, err := tfcognitoidp.FindCognitoUserInGroup(conn, groupName, userPoolId, username) + + if tfawserr.ErrCodeEquals(err, cognitoidentityprovider.ErrCodeResourceNotFoundException) { + continue + } + + if err != nil { + return err + } + + if found { + return fmt.Errorf("user in group still exists (%s)", rs.Primary.ID) + } + } + + return nil +} diff --git a/website/docs/r/cognito_user_in_group.html.markdown b/website/docs/r/cognito_user_in_group.html.markdown new file mode 100644 index 00000000000..9edbbb3c11a --- /dev/null +++ b/website/docs/r/cognito_user_in_group.html.markdown @@ -0,0 +1,55 @@ +--- +subcategory: "Cognito" +layout: "aws" +page_title: "AWS: aws_cognito_user_in_group" +description: |- + Adds the specified user to the specified group. +--- + +# Resource: aws_cognito_user_in_group + +Adds the specified user to the specified group. + +## Example Usage + +```terraform +resource "aws_cognito_user_pool" "example" { + name = "example" + + password_policy { + temporary_password_validity_days = 7 + minimum_length = 6 + require_uppercase = false + require_symbols = false + require_numbers = false + } +} + +resource "aws_cognito_user" "example" { + user_pool_id = aws_cognito_user_pool.test.id + username = "example" +} + +resource "aws_cognito_user_group" "example" { + user_pool_id = aws_cognito_user_pool.test.id + name = "example" +} + +resource "aws_cognito_user_in_group" "example" { + user_pool_id = aws_cognito_user_pool.example.id + group_name = aws_cognito_user_group.example.name + username = aws_cognito_user.example.username +} +``` + +## Argument Reference + +The following arguments are required: + +* `user_pool_id` - (Required) The user pool ID of the user and group. +* `group_name` - (Required) The name of the group to which the user is to be added. +* `username` - (Required) The username of the user to be added to the group. + +## Attributes Reference + +No additional attributes are exported.