diff --git a/.changelog/31681.txt b/.changelog/31681.txt new file mode 100644 index 00000000000..688538c5e37 --- /dev/null +++ b/.changelog/31681.txt @@ -0,0 +1,3 @@ +```release-note:new-data-source +aws_identitystore_groups +``` diff --git a/internal/service/identitystore/groups_data_source.go b/internal/service/identitystore/groups_data_source.go new file mode 100644 index 00000000000..1d6b828776f --- /dev/null +++ b/internal/service/identitystore/groups_data_source.go @@ -0,0 +1,127 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package identitystore + +import ( + "context" + + "github.com/YakDriver/regexache" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/identitystore" + "github.com/aws/aws-sdk-go-v2/service/identitystore/types" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "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" + "github.com/hashicorp/terraform-provider-aws/internal/create" + "github.com/hashicorp/terraform-provider-aws/names" +) + +// @SDKDataSource("aws_identitystore_groups", name="Groups") +func DataSourceGroups() *schema.Resource { + return &schema.Resource{ + ReadWithoutTimeout: dataSourceGroupsRead, + + Schema: map[string]*schema.Schema{ + "groups": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "description": { + Type: schema.TypeString, + Computed: true, + }, + "display_name": { + Type: schema.TypeString, + Computed: true, + }, + "external_ids": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "id": { + Type: schema.TypeString, + Computed: true, + }, + "issuer": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "group_id": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "identity_store_id": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.All( + validation.StringLenBetween(1, 64), + validation.StringMatch(regexache.MustCompile(`^[a-zA-Z0-9-]*$`), "must match [a-zA-Z0-9-]"), + ), + }, + }, + } +} + +const ( + DSNameGroups = "Groups Data Source" +) + +func dataSourceGroupsRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*conns.AWSClient).IdentityStoreClient(ctx) + + identityStoreID := d.Get("identity_store_id").(string) + + input := &identitystore.ListGroupsInput{ + IdentityStoreId: aws.String(identityStoreID), + } + + paginator := identitystore.NewListGroupsPaginator(conn, input) + groups := make([]types.Group, 0) + + for paginator.HasMorePages() { + page, err := paginator.NextPage(ctx) + + if err != nil { + return create.DiagError(names.IdentityStore, create.ErrActionReading, DSNameGroups, identityStoreID, err) + } + + groups = append(groups, page.Groups...) + } + + d.SetId(identityStoreID) + + if err := d.Set("groups", flattenGroups(groups)); err != nil { + return create.DiagError(names.IdentityStore, create.ErrActionSetting, DSNameGroups, d.Id(), err) + } + + return nil +} + +func flattenGroups(groups []types.Group) []map[string]interface{} { + if len(groups) == 0 { + return nil + } + + result := make([]map[string]interface{}, 0) + for _, group := range groups { + g := make(map[string]interface{}, 1) + g["description"] = aws.ToString(group.Description) + g["display_name"] = aws.ToString(group.DisplayName) + g["external_ids"] = flattenExternalIds(group.ExternalIds) + g["group_id"] = aws.ToString(group.GroupId) + + result = append(result, g) + } + + return result +} diff --git a/internal/service/identitystore/groups_data_source_test.go b/internal/service/identitystore/groups_data_source_test.go new file mode 100644 index 00000000000..038d3836573 --- /dev/null +++ b/internal/service/identitystore/groups_data_source_test.go @@ -0,0 +1,89 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package identitystore_test + +import ( + "errors" + "fmt" + "strconv" + "testing" + + 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-provider-aws/internal/acctest" + "github.com/hashicorp/terraform-provider-aws/internal/create" + tfidentitystore "github.com/hashicorp/terraform-provider-aws/internal/service/identitystore" + "github.com/hashicorp/terraform-provider-aws/names" +) + +func TestAccIdentityStoreGroupsDataSource_basic(t *testing.T) { + ctx := acctest.Context(t) + + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) // Group Name + dataSourceName := "data.aws_identitystore_groups.test" + groupResourceName := "aws_identitystore_group.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + acctest.PreCheck(ctx, t) + testAccPreCheckSSOAdminInstances(ctx, t) + }, + ErrorCheck: acctest.ErrorCheck(t, names.IdentityStoreEndpointID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + // All destroy checks are already covered in sibling tests + CheckDestroy: acctest.CheckDestroyNoop, + Steps: []resource.TestStep{ + { + Config: testAccConfigGroups_basic(rName), + // IdentityStore instances are account wide and there's no reasonable + // way to pick a specific resource with the built-in helpers so, custom + // test function time. + Check: testAccCheckGroupsAttributes(dataSourceName, groupResourceName), + }, + }, + }) +} + +func testAccCheckGroupsAttributes(dsName, rsName string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[rsName] + if !ok { + return create.Error(names.IdentityStore, create.ErrActionCheckingExistence, tfidentitystore.ResNameGroup, rsName, errors.New("not found")) + } + + ds, ok := s.RootModule().Resources[dsName] + if !ok { + return create.Error(names.IdentityStore, create.ErrActionCheckingExistence, tfidentitystore.DSNameGroups, dsName, errors.New("not found")) + } + + for i, _ := strconv.Atoi(ds.Primary.Attributes["groups.#"]); i >= 0; i-- { + if ds.Primary.Attributes[fmt.Sprintf("groups.%d.group_id", i)] == rs.Primary.Attributes["group_id"] && + ds.Primary.Attributes[fmt.Sprintf("groups.%d.display_name", i)] == rs.Primary.Attributes["display_name"] && + ds.Primary.Attributes[fmt.Sprintf("groups.%d.description", i)] == rs.Primary.Attributes["description"] { + return nil + } + } + + return create.Error(names.IdentityStore, create.ErrActionCheckingExistence, tfidentitystore.DSNameGroups, dsName, errors.New("couldn't find rgroup in output groups or attributes were mismatched")) + } +} + +func testAccConfigGroups_basic(groupName string) string { + return fmt.Sprintf(` +data "aws_ssoadmin_instances" "test" {} + +resource "aws_identitystore_group" "test" { + identity_store_id = tolist(data.aws_ssoadmin_instances.test.identity_store_ids)[0] + display_name = %[1]q + description = "Acceptance Test" +} + +data "aws_identitystore_groups" "test" { + depends_on = [aws_identitystore_group.test] + + identity_store_id = tolist(data.aws_ssoadmin_instances.test.identity_store_ids)[0] +} +`, groupName) +} diff --git a/internal/service/identitystore/service_package_gen.go b/internal/service/identitystore/service_package_gen.go index e0d9ea2f365..acd52518a41 100644 --- a/internal/service/identitystore/service_package_gen.go +++ b/internal/service/identitystore/service_package_gen.go @@ -28,6 +28,11 @@ func (p *servicePackage) SDKDataSources(ctx context.Context) []*types.ServicePac Factory: DataSourceGroup, TypeName: "aws_identitystore_group", }, + { + Factory: DataSourceGroups, + TypeName: "aws_identitystore_groups", + Name: "Groups", + }, { Factory: DataSourceUser, TypeName: "aws_identitystore_user", diff --git a/website/docs/d/identitystore_groups.html.markdown b/website/docs/d/identitystore_groups.html.markdown new file mode 100644 index 00000000000..c15ec84ec26 --- /dev/null +++ b/website/docs/d/identitystore_groups.html.markdown @@ -0,0 +1,41 @@ +--- +subcategory: "SSO Identity Store" +layout: "aws" +page_title: "AWS: aws_identitystore_groups" +description: |- + Retrieve list of groups for an Identity Store instance. +--- + +# Data Source: aws_identitystore_groups + +Use this data source to get a list of groups in an Identity Store instance. + +## Example Usage + +### Basic Usage + +```terraform +data "aws_ssoadmin_instances" "example" {} + +data "aws_identitystore_groups" "example" { + identity_store_id = tolist(data.aws_ssoadmin_instances.example.identity_store_ids)[0] +} +``` + +## Argument Reference + +The following arguments are required: + +* `identity_store_id` - (Required) Identity Store ID associated with the Single Sign-On Instance. + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +* `groups` - List of Identity Store Groups + * `group_id` - Identifier of the group in the Identity Store. + * `description` - Description of the specified group. + * `display_name` - Group's display name value. + * `external_ids` - List of identifiers issued to this resource by an external identity provider. + * `id` - The identifier issued to this resource by an external identity provider. + * `issuer` - The issuer for an external identifier.